d04467018e
## Summary The platform notifications admin page can now save a notification as a draft without committing to a schedule, then publish it later by entering start and end dates. Drafts stay hidden from the webapp panel, the CLI, and the "What's new" changelog until they are published. ## Design A draft is an `isDraft` flag on `PlatformNotification`, not nullable dates, so the existing index and every read query stay intact. All three reader queries filter on the flag, so a draft can never surface regardless of its placeholder dates. Publishing writes the real start and end dates and clears the flag; the publish dialog validates the range and shows inline errors. Editing a draft keeps it a draft, with the schedule fields hidden until publish. Also folds in a small tweak: the "Send preview to me" test button now appears when editing a notification, not just when creating one.
3370 lines
102 KiB
Plaintext
3370 lines
102 KiB
Plaintext
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
directUrl = env("DIRECT_URL")
|
||
}
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
output = "../generated/prisma"
|
||
binaryTargets = ["native", "debian-openssl-1.1.x"]
|
||
previewFeatures = ["metrics", "driverAdapters"]
|
||
}
|
||
|
||
model User {
|
||
id String @id @default(cuid())
|
||
email String @unique
|
||
|
||
authenticationMethod AuthenticationMethod
|
||
authenticationProfile Json?
|
||
authenticationExtraParams Json?
|
||
authIdentifier String? @unique
|
||
|
||
displayName String?
|
||
name String?
|
||
avatarUrl String?
|
||
|
||
admin Boolean @default(false)
|
||
|
||
/// Preferences for the dashboard
|
||
dashboardPreferences Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// @deprecated
|
||
isOnCloudWaitlist Boolean @default(false)
|
||
/// @deprecated
|
||
featureCloud Boolean @default(false)
|
||
/// @deprecated
|
||
isOnHostedRepoWaitlist Boolean @default(false)
|
||
|
||
marketingEmails Boolean @default(true)
|
||
confirmedBasicDetails Boolean @default(false)
|
||
|
||
referralSource String?
|
||
onboardingData Json?
|
||
|
||
orgMemberships OrgMember[]
|
||
sentInvites OrgMemberInvite[]
|
||
|
||
mfaEnabledAt DateTime?
|
||
mfaSecretReference SecretReference? @relation(fields: [mfaSecretReferenceId], references: [id])
|
||
mfaSecretReferenceId String?
|
||
/// Hash of the last used code to prevent replay attacks
|
||
mfaLastUsedCode String?
|
||
|
||
/// Maximum session lifetime in seconds for this user. Default = 1 year (Gregorian).
|
||
/// May be further restricted by Organization.maxSessionDuration on any of the user's orgs.
|
||
sessionDuration Int @default(31556952)
|
||
|
||
/// Absolute timestamp at which the user's current session must end. Written
|
||
/// at login (and any time the effective duration is recomputed) and shortened
|
||
/// when an admin lowers an org cap. Read on requireUser / getUser; null is
|
||
/// treated as "no enforced deadline" (legacy sessions from before this
|
||
/// shipped). Read-on-demand has zero per-request DB cost — the value
|
||
/// piggybacks on the User fetch that requireUser already does.
|
||
nextSessionEnd DateTime?
|
||
|
||
invitationCode InvitationCode? @relation(fields: [invitationCodeId], references: [id])
|
||
invitationCodeId String?
|
||
personalAccessTokens PersonalAccessToken[]
|
||
createdApiKeys ApiKey[]
|
||
deployments WorkerDeployment[]
|
||
backupCodes MfaBackupCode[]
|
||
bulkActions BulkActionGroup[]
|
||
|
||
impersonationsPerformed ImpersonationAuditLog[] @relation("ImpersonationAdmin")
|
||
impersonationsReceived ImpersonationAuditLog[] @relation("ImpersonationTarget")
|
||
customerQueries CustomerQuery[]
|
||
metricsDashboards MetricsDashboard[]
|
||
platformNotifications PlatformNotification[]
|
||
platformNotificationInteractions PlatformNotificationInteraction[]
|
||
}
|
||
|
||
model MfaBackupCode {
|
||
id String @id @default(cuid())
|
||
/// Hash of the actual code
|
||
code String
|
||
|
||
user User @relation(fields: [userId], references: [id])
|
||
userId String
|
||
|
||
usedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([userId, code])
|
||
}
|
||
|
||
// @deprecated This model is no longer used as the Cloud is out of private beta
|
||
// Leaving it here for now for historical reasons
|
||
model InvitationCode {
|
||
id String @id @default(cuid())
|
||
code String @unique
|
||
|
||
users User[]
|
||
|
||
createdAt DateTime @default(now())
|
||
}
|
||
|
||
enum AuthenticationMethod {
|
||
GITHUB
|
||
MAGIC_LINK
|
||
GOOGLE
|
||
SSO
|
||
}
|
||
|
||
/// Used to generate PersonalAccessTokens, they're one-time use
|
||
model AuthorizationCode {
|
||
id String @id @default(cuid())
|
||
|
||
code String @unique
|
||
|
||
personalAccessToken PersonalAccessToken? @relation(fields: [personalAccessTokenId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
personalAccessTokenId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
// Used by User's to perform API actions
|
||
model PersonalAccessToken {
|
||
id String @id @default(cuid())
|
||
|
||
/// If generated by the CLI this will be "cli", otherwise user-provided
|
||
name String
|
||
|
||
/// This is the token encrypted using the ENCRYPTION_KEY
|
||
encryptedToken Json
|
||
|
||
/// This is shown in the UI, with ********
|
||
obfuscatedToken String
|
||
|
||
/// This is used to find the token in the database
|
||
hashedToken String @unique
|
||
|
||
user User @relation(fields: [userId], references: [id])
|
||
userId String
|
||
|
||
revokedAt DateTime?
|
||
lastAccessedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
authorizationCodes AuthorizationCode[]
|
||
|
||
@@index([userId])
|
||
}
|
||
|
||
enum OrganizationAccessTokenType {
|
||
USER
|
||
SYSTEM
|
||
}
|
||
|
||
model OrganizationAccessToken {
|
||
id String @id @default(cuid())
|
||
|
||
/// User-provided name for the token
|
||
name String
|
||
|
||
/// Used to differentiate between user-generated and system-generated tokens
|
||
type OrganizationAccessTokenType @default(USER)
|
||
|
||
/// This is used to find the token in the database
|
||
hashedToken String @unique
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id])
|
||
organizationId String
|
||
|
||
/// Optional expiration date for the token
|
||
expiresAt DateTime?
|
||
revokedAt DateTime?
|
||
lastAccessedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([organizationId, type, createdAt(sort: Desc)])
|
||
}
|
||
|
||
model Organization {
|
||
id String @id @default(cuid())
|
||
slug String @unique
|
||
title String
|
||
|
||
maximumExecutionTimePerRunInMs Int @default(900000) // 15 minutes
|
||
maximumConcurrencyLimit Int @default(10)
|
||
/// This is deprecated and will be removed in the future
|
||
maximumSchedulesLimit Int @default(5)
|
||
|
||
maximumDevQueueSize Int?
|
||
maximumDeployedQueueSize Int?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
companySize String?
|
||
|
||
onboardingData Json?
|
||
|
||
avatar Json?
|
||
|
||
runsEnabled Boolean @default(true)
|
||
|
||
isActivated Boolean @default(false) @map("v3Enabled")
|
||
|
||
/// @deprecated
|
||
v2Enabled Boolean @default(false)
|
||
/// @deprecated
|
||
v2MarqsEnabled Boolean @default(false)
|
||
/// @deprecated
|
||
hasRequestedV3 Boolean @default(false)
|
||
|
||
environments RuntimeEnvironment[]
|
||
|
||
apiRateLimiterConfig Json?
|
||
realtimeRateLimiterConfig Json?
|
||
|
||
batchRateLimitConfig Json?
|
||
batchQueueConcurrencyConfig Json?
|
||
|
||
featureFlags Json?
|
||
|
||
maximumProjectCount Int @default(25)
|
||
|
||
/// Maximum session lifetime in seconds for members of this organization.
|
||
/// When set, caps each member's User.sessionDuration. The most restrictive cap across
|
||
/// all of a user's orgs (excluding nulls) wins.
|
||
maxSessionDuration Int?
|
||
|
||
projects Project[]
|
||
members OrgMember[]
|
||
invites OrgMemberInvite[]
|
||
organizationIntegrations OrganizationIntegration[]
|
||
organizationAccessTokens OrganizationAccessToken[]
|
||
workerGroups WorkerInstanceGroup[]
|
||
workerInstances WorkerInstance[]
|
||
githubAppInstallations GithubAppInstallation[]
|
||
customerQueries CustomerQuery[]
|
||
metricsDashboards MetricsDashboard[]
|
||
prompts Prompt[]
|
||
|
||
platformNotifications PlatformNotification[]
|
||
errorGroupStates ErrorGroupState[]
|
||
|
||
/// S2 basin that holds this org's realtime streams. Null until the
|
||
/// per-org basin has been provisioned (OSS / s2-lite installs leave
|
||
/// it null forever; reads fall back to the global basin env var).
|
||
/// Set once at provisioning time; retention is reconfigured in-place
|
||
/// when the org's plan changes.
|
||
streamBasinName String?
|
||
}
|
||
|
||
model OrgMember {
|
||
id String @id @default(cuid())
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
userId String
|
||
|
||
role OrgMemberRole @default(MEMBER)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
environments RuntimeEnvironment[]
|
||
|
||
@@unique([organizationId, userId])
|
||
}
|
||
|
||
enum OrgMemberRole {
|
||
ADMIN
|
||
MEMBER
|
||
}
|
||
|
||
model OrgMemberInvite {
|
||
id String @id @default(cuid())
|
||
token String @unique @default(cuid())
|
||
email String
|
||
role OrgMemberRole @default(MEMBER)
|
||
|
||
/// Optional RBAC role to assign on invite acceptance. When set, the
|
||
/// accept-invite flow calls the loaded RBAC plugin's setUserRole with
|
||
/// this id after creating the OrgMember. Null = legacy behaviour, the
|
||
/// runtime fallback derives the role from `role` above.
|
||
///
|
||
/// Plain text (not an FK) — the RBAC plugin's RbacRole table lives on
|
||
/// a separate schema (Drizzle, not Prisma) so we can't model the FK
|
||
/// here. Validation happens at write time (action) and read time
|
||
/// (acceptInvite).
|
||
rbacRoleId String?
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
inviter User @relation(fields: [inviterId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
inviterId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([organizationId, email])
|
||
}
|
||
|
||
model RuntimeEnvironment {
|
||
id String @id @default(cuid())
|
||
slug String
|
||
apiKey String @unique
|
||
|
||
/// @deprecated was for v2
|
||
pkApiKey String @unique
|
||
|
||
type RuntimeEnvironmentType @default(DEVELOPMENT)
|
||
|
||
// Preview branches
|
||
/// If true, this environment has branches and is treated differently in the dashboard/API
|
||
/// NB: this flag is NOT used for Development branches, instead (type, parentEnvironmentId) = (DEVELOPMENT, NULL) is used
|
||
isBranchableEnvironment Boolean @default(false)
|
||
branchName String?
|
||
parentEnvironment RuntimeEnvironment? @relation("parentEnvironment", fields: [parentEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
parentEnvironmentId String?
|
||
childEnvironments RuntimeEnvironment[] @relation("parentEnvironment")
|
||
|
||
// This is GitMeta type
|
||
git Json?
|
||
|
||
/// When set API calls will fail
|
||
archivedAt DateTime?
|
||
|
||
///A memorable code for the environment
|
||
shortcode String
|
||
|
||
maximumConcurrencyLimit Int @default(5)
|
||
concurrencyLimitBurstFactor Decimal @default("2.00") @db.Decimal(4, 2)
|
||
paused Boolean @default(false)
|
||
pauseSource EnvironmentPauseSource?
|
||
|
||
autoEnableInternalSources Boolean @default(true)
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
//when the org member is deleted, it will keep the environment but set it to null
|
||
orgMember OrgMember? @relation(fields: [orgMemberId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
orgMemberId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// Allows us to customize the built-in environment variables for a specific environment, like TRIGGER_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT
|
||
builtInEnvironmentVariableOverrides Json?
|
||
|
||
tunnelId String?
|
||
|
||
backgroundWorkers BackgroundWorker[]
|
||
backgroundWorkerTasks BackgroundWorkerTask[]
|
||
taskRuns TaskRun[]
|
||
taskQueues TaskQueue[]
|
||
environmentVariableValues EnvironmentVariableValue[]
|
||
checkpoints Checkpoint[]
|
||
workerDeployments WorkerDeployment[]
|
||
workerDeploymentPromotions WorkerDeploymentPromotion[]
|
||
taskRunAttempts TaskRunAttempt[]
|
||
CheckpointRestoreEvent CheckpointRestoreEvent[]
|
||
taskScheduleInstances TaskScheduleInstance[]
|
||
alerts ProjectAlert[]
|
||
|
||
sessions RuntimeEnvironmentSession[]
|
||
currentSession RuntimeEnvironmentSession? @relation("currentSession", fields: [currentSessionId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
currentSessionId String?
|
||
taskRunNumberCounter TaskRunNumberCounter[]
|
||
taskRunCheckpoints TaskRunCheckpoint[]
|
||
waitpoints Waitpoint[]
|
||
workerInstances WorkerInstance[]
|
||
waitpointTags WaitpointTag[]
|
||
BulkActionGroup BulkActionGroup[]
|
||
customerQueries CustomerQuery[]
|
||
prompts Prompt[]
|
||
playgroundConversations PlaygroundConversation[]
|
||
errorGroupStates ErrorGroupState[]
|
||
taskIdentifiers TaskIdentifier[]
|
||
apiKeys ApiKey[]
|
||
revokedApiKeys RevokedApiKey[]
|
||
|
||
// A partial unique index also enforces one STAGING/PREVIEW root per project and type.
|
||
// It is defined in SQL because Prisma does not support partial indexes in the schema.
|
||
@@unique([projectId, slug, orgMemberId])
|
||
@@unique([projectId, shortcode])
|
||
@@index([parentEnvironmentId])
|
||
@@index([projectId])
|
||
@@index([organizationId])
|
||
}
|
||
|
||
model ApiKey {
|
||
id String @id @default(cuid())
|
||
name String
|
||
keyHash String @unique @map("key_hash")
|
||
lastFour String @map("last_four")
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String @map("runtime_environment_id")
|
||
|
||
createdBy User? @relation(fields: [createdByUserId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
createdByUserId String? @map("created_by_user_id")
|
||
|
||
presetId String? @map("preset_id")
|
||
scopes String[]
|
||
|
||
lastUsedAt DateTime? @map("last_used_at")
|
||
revokedAt DateTime? @map("revoked_at")
|
||
expiresAt DateTime? @map("expires_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
|
||
@@index([runtimeEnvironmentId, revokedAt, createdAt(sort: Desc)])
|
||
@@map("api_keys")
|
||
}
|
||
|
||
/// Records of previously-valid API keys that are still accepted for authentication
|
||
/// during a grace window after rotation. Extend or end the grace period by updating `expiresAt`.
|
||
model RevokedApiKey {
|
||
id String @id @default(cuid())
|
||
apiKey String
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
expiresAt DateTime
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([apiKey])
|
||
@@index([runtimeEnvironmentId])
|
||
}
|
||
|
||
enum RuntimeEnvironmentType {
|
||
PRODUCTION
|
||
STAGING
|
||
DEVELOPMENT
|
||
PREVIEW
|
||
}
|
||
|
||
enum EnvironmentPauseSource {
|
||
BILLING_LIMIT
|
||
}
|
||
|
||
model Project {
|
||
id String @id @default(cuid())
|
||
slug String @unique
|
||
name String
|
||
|
||
externalRef String @unique
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
/// Set the first time the CLI `init` command completes against this project. Drives the dev onboarding progress.
|
||
initializedAt DateTime?
|
||
|
||
/// Runtime used when a deployment config does not specify one. Null preserves the legacy Node 20 fallback.
|
||
defaultRuntime String?
|
||
|
||
version ProjectVersion @default(V2)
|
||
engine RunEngineVersion @default(V1)
|
||
|
||
builderProjectId String?
|
||
|
||
workerGroups WorkerInstanceGroup[]
|
||
workers WorkerInstance[]
|
||
|
||
defaultWorkerGroup WorkerInstanceGroup? @relation("ProjectDefaultWorkerGroup", fields: [defaultWorkerGroupId], references: [id])
|
||
defaultWorkerGroupId String?
|
||
|
||
/// The master queues they are allowed to use (impacts what they can set as default and trigger runs with)
|
||
allowedWorkerQueues String[] @default([]) @map("allowedMasterQueues")
|
||
|
||
environments RuntimeEnvironment[]
|
||
backgroundWorkers BackgroundWorker[]
|
||
backgroundWorkerTasks BackgroundWorkerTask[]
|
||
taskRuns TaskRun[]
|
||
runTags TaskRunTag[]
|
||
taskQueues TaskQueue[]
|
||
environmentVariables EnvironmentVariable[]
|
||
checkpoints Checkpoint[]
|
||
WorkerDeployment WorkerDeployment[]
|
||
CheckpointRestoreEvent CheckpointRestoreEvent[]
|
||
taskSchedules TaskSchedule[]
|
||
alertChannels ProjectAlertChannel[]
|
||
alerts ProjectAlert[]
|
||
alertStorages ProjectAlertStorage[]
|
||
bulkActionGroups BulkActionGroup[]
|
||
BackgroundWorkerFile BackgroundWorkerFile[]
|
||
waitpoints Waitpoint[]
|
||
taskRunWaitpoints TaskRunWaitpoint[]
|
||
taskRunCheckpoints TaskRunCheckpoint[]
|
||
waitpointTags WaitpointTag[]
|
||
connectedGithubRepository ConnectedGithubRepository?
|
||
organizationProjectIntegration OrganizationProjectIntegration[]
|
||
customerQueries CustomerQuery[]
|
||
playgroundConversations PlaygroundConversation[]
|
||
|
||
buildSettings Json?
|
||
onboardingData Json?
|
||
taskScheduleInstances TaskScheduleInstance[]
|
||
metricsDashboards MetricsDashboard[]
|
||
llmModels LlmModel[]
|
||
prompts Prompt[]
|
||
platformNotifications PlatformNotification[]
|
||
errorGroupStates ErrorGroupState[]
|
||
taskIdentifiers TaskIdentifier[]
|
||
}
|
||
|
||
enum ProjectVersion {
|
||
V2
|
||
V3
|
||
}
|
||
|
||
model SecretReference {
|
||
id String @id @default(cuid())
|
||
key String @unique
|
||
provider SecretStoreProvider @default(DATABASE)
|
||
|
||
environmentVariableValues EnvironmentVariableValue[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
OrganizationIntegration OrganizationIntegration[]
|
||
User User[]
|
||
}
|
||
|
||
enum SecretStoreProvider {
|
||
DATABASE
|
||
AWS_PARAM_STORE
|
||
}
|
||
|
||
model SecretStore {
|
||
key String @unique
|
||
value Json
|
||
version String @default("1")
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([key(ops: raw("text_pattern_ops"))], type: BTree)
|
||
}
|
||
|
||
model DataMigration {
|
||
id String @id @default(cuid())
|
||
name String @unique
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
completedAt DateTime?
|
||
}
|
||
|
||
// ====================================================
|
||
// v3 Models
|
||
// ====================================================
|
||
model BackgroundWorker {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
engine RunEngineVersion @default(V1)
|
||
|
||
contentHash String
|
||
sdkVersion String @default("unknown")
|
||
cliVersion String @default("unknown")
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
version String
|
||
metadata Json
|
||
|
||
runtime String?
|
||
runtimeVersion String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
tasks BackgroundWorkerTask[]
|
||
attempts TaskRunAttempt[]
|
||
lockedRuns TaskRun[]
|
||
files BackgroundWorkerFile[]
|
||
queues TaskQueue[]
|
||
promptVersions PromptVersion[]
|
||
|
||
deployment WorkerDeployment?
|
||
|
||
workerGroup WorkerInstanceGroup? @relation(fields: [workerGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
workerGroupId String?
|
||
|
||
supportsLazyAttempts Boolean @default(false)
|
||
|
||
taskIdentifiers TaskIdentifier[]
|
||
|
||
@@unique([projectId, runtimeEnvironmentId, version])
|
||
@@index([runtimeEnvironmentId])
|
||
// Get the latest worker for a given environment
|
||
@@index([runtimeEnvironmentId, createdAt(sort: Desc)])
|
||
}
|
||
|
||
model BackgroundWorkerFile {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
filePath String
|
||
contentHash String
|
||
contents Bytes
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
backgroundWorkers BackgroundWorker[]
|
||
|
||
tasks BackgroundWorkerTask[]
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([projectId, contentHash])
|
||
}
|
||
|
||
model Prompt {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique @map("friendly_id")
|
||
slug String
|
||
description String?
|
||
type String @default("text") // "text" | "chat"
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
filePath String?
|
||
exportName String?
|
||
variableSchema Json?
|
||
defaultModel String?
|
||
defaultConfig Json?
|
||
|
||
tags String[] @default([])
|
||
archivedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
versions PromptVersion[]
|
||
|
||
@@unique([projectId, runtimeEnvironmentId, slug])
|
||
@@index([projectId])
|
||
@@index([runtimeEnvironmentId])
|
||
@@map("prompts")
|
||
}
|
||
|
||
model PromptVersion {
|
||
id String @id @default(cuid())
|
||
version Int
|
||
|
||
prompt Prompt @relation(fields: [promptId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
promptId String
|
||
|
||
textContent String?
|
||
chatContent Json?
|
||
|
||
model String?
|
||
config Json?
|
||
|
||
source String // "deploy" | "dashboard" | "api"
|
||
commitMessage String?
|
||
contentHash String
|
||
|
||
labels String[] @default([])
|
||
|
||
worker BackgroundWorker? @relation(fields: [workerId], references: [id])
|
||
workerId String?
|
||
|
||
createdBy String?
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([promptId, version])
|
||
@@index([promptId])
|
||
@@index([contentHash])
|
||
@@map("prompt_versions")
|
||
}
|
||
|
||
model BackgroundWorkerTask {
|
||
id String @id @default(cuid())
|
||
slug String
|
||
|
||
description String?
|
||
|
||
friendlyId String @unique
|
||
|
||
filePath String
|
||
exportName String?
|
||
|
||
worker BackgroundWorker @relation(fields: [workerId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
workerId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
file BackgroundWorkerFile? @relation(fields: [fileId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
fileId String?
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
attempts TaskRunAttempt[]
|
||
runs TaskRun[]
|
||
|
||
queueConfig Json?
|
||
retryConfig Json?
|
||
machineConfig Json?
|
||
|
||
queueId String?
|
||
queue TaskQueue? @relation(fields: [queueId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
|
||
maxDurationInSeconds Int?
|
||
|
||
ttl String?
|
||
|
||
triggerSource TaskTriggerSource @default(STANDARD)
|
||
|
||
/// Extra task configuration JSON. Shape depends on triggerSource.
|
||
/// AGENT: { type: "ai-sdk-chat" }
|
||
config Json?
|
||
|
||
payloadSchema Json?
|
||
|
||
@@unique([workerId, slug])
|
||
// Quick lookup of task identifiers
|
||
@@index([projectId, slug])
|
||
@@index([projectId, slug, createdAt])
|
||
@@index([runtimeEnvironmentId, projectId])
|
||
@@index([runtimeEnvironmentId, slug, triggerSource])
|
||
}
|
||
|
||
enum TaskTriggerSource {
|
||
STANDARD
|
||
SCHEDULED
|
||
AGENT
|
||
WEBHOOK
|
||
}
|
||
|
||
model WebhookEndpoint {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique // wh_... (WebhookEndpointId IdUtil "wh")
|
||
|
||
/// URL key. CSPRNG, NOT a friendlyId: crypto.randomBytes(16) base64url.
|
||
/// Generated by the create path (deploy-sync / P2 API), NOT by @default.
|
||
opaqueId String @unique // global unique (Q2): ingress resolves the endpoint by opaqueId alone
|
||
|
||
// scope (plain columns, NO @relation -> no FK, matching TaskEventPartitioned)
|
||
organizationId String
|
||
projectId String
|
||
runtimeEnvironmentId String
|
||
environmentType RuntimeEnvironmentType // denormalized from the env at deploy-sync (immutable per endpoint), Q3
|
||
|
||
// tenant scope. "" sentinel on declared (P1); P2 dynamic create fills these.
|
||
// Sentinel not null so the 4-column @@unique actually bites.
|
||
endpointTenantId String @default("")
|
||
endpointExternalRef String @default("")
|
||
|
||
source String // provider tag e.g. "stripe","slack","github"
|
||
handlerWebhookId String // declared webhook() id (string ref, GOLDEN LAW, no relation)
|
||
routingTarget Json // RoutingTarget tagged union ({ type: "task" } | { type: "session" })
|
||
verifierArtifact Json // VerifierArtifact tagged union (config|preset in P1)
|
||
filter String? // source filter DSL string (display/round-trip)
|
||
filterAst Json? // compiled FilterAst, evaluated at ingest; null = route all
|
||
filterAstVersion Int? // re-parse `filter` on a format bump
|
||
metadata Json @default("{}") // arbitrary user metadata; flows into the webhook task
|
||
|
||
// who supplies the secret/key; drives the Connect UI (paste vs generate). From the source.
|
||
secretProvisioning String @default("either") // "provider" | "integrator" | "either"
|
||
|
||
/// SecretReference.key string. Plain String, NO @relation -> no FK to SecretReference.
|
||
signingSecretKey String?
|
||
|
||
status WebhookEndpointStatus @default(ACTIVE)
|
||
/// When an operator disabled the endpoint via the dashboard/API. Null means the declarative sync
|
||
/// owns the status: a redeploy that re-declares a previously-removed (auto-deactivated) webhook
|
||
/// reactivates it. Non-null means the operator disabled it, so the sync leaves the status alone.
|
||
manuallyDeactivatedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([runtimeEnvironmentId, handlerWebhookId, endpointTenantId, endpointExternalRef]) // deploy-sync key
|
||
@@index([runtimeEnvironmentId, source])
|
||
@@index([projectId, createdAt(sort: Desc)])
|
||
}
|
||
|
||
enum WebhookEndpointStatus {
|
||
ACTIVE
|
||
INACTIVE
|
||
DELETING
|
||
}
|
||
|
||
model WebhookDelivery {
|
||
id String @default(cuid())
|
||
friendlyId String // whd_... (WebhookDeliveryId IdUtil "whd"). Uniqueness scoped by the composite PK.
|
||
|
||
// plain columns, NO @relation -> no FK
|
||
webhookEndpointId String
|
||
organizationId String
|
||
projectId String
|
||
runtimeEnvironmentId String
|
||
environmentType RuntimeEnvironmentType
|
||
|
||
externalDeliveryId String // provider id (Stripe event id, GitHub X-GitHub-Delivery)
|
||
idempotencyKey String // = externalDeliveryId; passed to Run Engine when routing to a task
|
||
runId String? // the triggered run, if task target
|
||
|
||
status WebhookDeliveryStatus @default(PENDING)
|
||
|
||
/// Set from the x-trigger-test ingress header; marks console/test-send deliveries so the list can filter them.
|
||
isTest Boolean @default(false)
|
||
|
||
parsedEvent Json? // size-capped snapshot of the verified event (full event lives in ClickHouse)
|
||
headers Json? // inbound request headers, surfaced to the webhook task via onEvent({ headers })
|
||
rawBodyHash String? // sha256 of raw bytes; cheap P2 replay anchor
|
||
errorMessage String?
|
||
filterReason String? // why a FILTERED delivery was not routed (failing clause + actual value)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt // Q14: real column; the status transitions update it, maps straight to CH updated_at
|
||
processedAt DateTime?
|
||
|
||
@@id([id, createdAt]) // composite PK: the partition key MUST be in every PK/unique on a partitioned table
|
||
@@index([webhookEndpointId, createdAt(sort: Desc)]) // dashboard recent-deliveries; also partition-pruned
|
||
}
|
||
|
||
enum WebhookDeliveryStatus {
|
||
PENDING
|
||
PROCESSING
|
||
SUCCEEDED
|
||
FAILED
|
||
FILTERED
|
||
}
|
||
|
||
model PlaygroundConversation {
|
||
id String @id @default(cuid())
|
||
|
||
/// The chat session ID used by the transport
|
||
chatId String
|
||
|
||
/// User-editable conversation title (auto-generated from first message)
|
||
title String @default("New conversation")
|
||
|
||
/// Which agent this conversation is with
|
||
agentSlug String
|
||
|
||
/// The current active run backing this conversation (null if no run yet)
|
||
runId String?
|
||
run TaskRun? @relation(fields: [runId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
|
||
/// The client data JSON used for this conversation
|
||
clientData Json?
|
||
|
||
/// Accumulated UIMessages from completed turns (for resume without stream replay)
|
||
messages Json?
|
||
|
||
/// Last SSE event ID — resume from this position to avoid replaying old turns
|
||
lastEventId String?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
/// The user who started this conversation
|
||
userId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([chatId, runtimeEnvironmentId])
|
||
@@index([runtimeEnvironmentId, agentSlug, updatedAt(sort: Desc)])
|
||
@@index([userId, runtimeEnvironmentId])
|
||
}
|
||
|
||
/// Durable, typed, bidirectional I/O primitive. Owns two S2 streams (.out / .in).
|
||
/// The row is essentially static — no status, no counters, no pointers. No
|
||
/// foreign keys: project/runtimeEnvironment/organization ids are plain
|
||
/// scalar columns (matches TaskRun pattern). List-style queries are served
|
||
/// from ClickHouse, not Postgres, so only point-lookup indexes live here.
|
||
model Session {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique
|
||
/// User-supplied identifier scoped to the environment. Used for
|
||
/// idempotent upsert and for resolving sessions via the public API.
|
||
externalId String?
|
||
|
||
/// Plain string — intentionally not an enum.
|
||
type String
|
||
|
||
/// Denormalized scoping columns — no FK relations.
|
||
projectId String
|
||
runtimeEnvironmentId String
|
||
environmentType RuntimeEnvironmentType
|
||
organizationId String
|
||
|
||
/// Task this session triggers runs against. Required — Sessions are
|
||
/// task-bound: creating a session also triggers its first run, and
|
||
/// every subsequent re-trigger uses this same identifier.
|
||
taskIdentifier String
|
||
|
||
/// Trigger config used for every run this session schedules. Shape
|
||
/// (validated at the route layer, opaque to the DB):
|
||
/// { basePayload: object, machine?: string, queue?: string,
|
||
/// tags?: string[], maxAttempts?: number,
|
||
/// idleTimeoutInSeconds?: number }
|
||
/// `basePayload` carries the customer's client-data; runtime fields
|
||
/// (chatId, messages, trigger) are merged at trigger time.
|
||
triggerConfig Json
|
||
|
||
/// Whether this session was created from the Test/playground UI rather
|
||
/// than by a real chat.agent() trigger. Mirrors TaskRun.isTest.
|
||
isTest Boolean @default(false)
|
||
|
||
tags String[] @default([])
|
||
metadata Json?
|
||
|
||
/// Live run pointer — non-FK so run deletion never cascades. Can lag
|
||
/// reality; the `.in/append` handler re-checks the snapshot status
|
||
/// before reusing it.
|
||
currentRunId String?
|
||
/// Monotonic counter used for optimistic locking on `currentRunId`
|
||
/// swaps. Bumped atomically alongside any update that changes
|
||
/// `currentRunId`.
|
||
currentRunVersion Int @default(0)
|
||
|
||
/// Terminal markers — written once, never flipped back.
|
||
closedAt DateTime?
|
||
closedReason String?
|
||
expiresAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// S2 basin where this session's stream pair lives. Stamped at create
|
||
/// time from `Organization.streamBasinName` so reads can resolve the
|
||
/// basin without joining org. Null when the org has no per-org basin
|
||
/// (OSS, or pre-backfill); reads fall back to the global basin.
|
||
streamBasinName String?
|
||
|
||
/// Storage URI (with protocol prefix) for this session's chat.agent
|
||
/// snapshot blob. Set on first snapshot write. Null = pre-column session,
|
||
/// fall back to computed default path.
|
||
chatSnapshotStoragePath String?
|
||
|
||
runs SessionRun[]
|
||
|
||
/// Idempotency: `(env, externalId)` uniquely identifies a session.
|
||
/// PostgreSQL treats NULLs as distinct, so `externalId=NULL` rows never collide.
|
||
@@unique([runtimeEnvironmentId, externalId])
|
||
@@index([expiresAt])
|
||
@@index([currentRunId])
|
||
}
|
||
|
||
/// Historical record of every run a Session has owned. Append-only —
|
||
/// rows are inserted on each `ensureRunForSession` claim, never updated.
|
||
/// Lets us reconstruct the run timeline of a chat for debugging /
|
||
/// dashboard surfaces. The relation cascades on Session delete (tied to
|
||
/// the session lifecycle) but `runId` is a plain string column with no
|
||
/// FK to TaskRun so run pruning is independent.
|
||
model SessionRun {
|
||
id String @id @default(cuid())
|
||
|
||
sessionId String
|
||
/// TaskRun.id (no FK — runs may be archived independently of session history)
|
||
runId String @unique
|
||
/// One of: "initial" | "continuation" | "upgrade" | "manual".
|
||
/// Plain string for forward-compat with future trigger reasons.
|
||
reason String
|
||
triggeredAt DateTime @default(now())
|
||
|
||
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||
|
||
@@index([sessionId])
|
||
}
|
||
|
||
model TaskRun {
|
||
id String @id @default(cuid())
|
||
|
||
number Int @default(0)
|
||
friendlyId String @unique
|
||
|
||
engine RunEngineVersion @default(V1)
|
||
|
||
status TaskRunStatus @default(PENDING)
|
||
statusReason String?
|
||
|
||
idempotencyKey String?
|
||
idempotencyKeyExpiresAt DateTime?
|
||
/// Stores the user-provided key and scope: { key: string, scope: "run" | "attempt" | "global" }
|
||
idempotencyKeyOptions Json?
|
||
|
||
/// Debounce options: { key: string, delay: string, createdAt: Date }
|
||
debounce Json?
|
||
|
||
taskIdentifier String
|
||
|
||
isTest Boolean @default(false)
|
||
|
||
payload String
|
||
payloadType String @default("application/json")
|
||
context Json?
|
||
traceContext Json?
|
||
|
||
traceId String
|
||
spanId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
environmentType RuntimeEnvironmentType?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
organizationId String?
|
||
|
||
// The specific queue this run is in
|
||
queue String
|
||
// The queueId is set when the run is locked to a specific queue
|
||
lockedQueueId String?
|
||
|
||
/// The main queue that this run is part of
|
||
workerQueue String @default("main") @map("masterQueue")
|
||
|
||
/// User-facing geo region, stamped at trigger; workerQueue is where it actually ran.
|
||
region String?
|
||
|
||
/// @deprecated
|
||
secondaryMasterQueue String?
|
||
|
||
/// From engine v2+ this will be defined after a run has been dequeued (starting at 1)
|
||
attemptNumber Int?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
attempts TaskRunAttempt[] @relation("attempts")
|
||
|
||
/// Denormized column that holds the raw tags
|
||
runTags String[]
|
||
|
||
/// Denormalized version of the background worker task
|
||
taskVersion String?
|
||
sdkVersion String?
|
||
cliVersion String?
|
||
|
||
checkpoints Checkpoint[]
|
||
|
||
/// startedAt marks the point at which a run is dequeued from MarQS
|
||
startedAt DateTime?
|
||
/// executedAt is set when the first attempt is about to execute
|
||
executedAt DateTime?
|
||
completedAt DateTime?
|
||
machinePreset String?
|
||
|
||
usageDurationMs Int @default(0)
|
||
costInCents Float @default(0)
|
||
baseCostInCents Float @default(0)
|
||
|
||
lockedAt DateTime?
|
||
lockedBy BackgroundWorkerTask? @relation(fields: [lockedById], references: [id])
|
||
lockedById String?
|
||
|
||
lockedToVersion BackgroundWorker? @relation(fields: [lockedToVersionId], references: [id])
|
||
lockedToVersionId String?
|
||
|
||
/// The "priority" of the run. This is just a negative offset in ms for the queue timestamp
|
||
/// E.g. a value of 60_000 would put the run into the queue 60s ago.
|
||
priorityMs Int @default(0)
|
||
|
||
concurrencyKey String?
|
||
|
||
delayUntil DateTime?
|
||
queuedAt DateTime?
|
||
ttl String?
|
||
expiredAt DateTime?
|
||
maxAttempts Int?
|
||
lockedRetryConfig Json?
|
||
|
||
/// optional token that can be used to authenticate the task run
|
||
oneTimeUseToken String?
|
||
|
||
///When this run is finished, the waitpoint will be marked as completed
|
||
associatedWaitpoint Waitpoint? @relation("CompletingRun")
|
||
|
||
///If there are any blocked waitpoints, the run won't be executed
|
||
blockedByWaitpoints TaskRunWaitpoint[]
|
||
|
||
/// All waitpoints that blocked this run at some point, used for display purposes
|
||
connectedWaitpoints Waitpoint[] @relation("WaitpointRunConnections")
|
||
|
||
/// Where the logs are stored
|
||
taskEventStore String @default("taskEvent")
|
||
|
||
queueTimestamp DateTime?
|
||
|
||
batchItems BatchTaskRunItem[]
|
||
dependency TaskRunDependency?
|
||
CheckpointRestoreEvent CheckpointRestoreEvent[]
|
||
executionSnapshots TaskRunExecutionSnapshot[]
|
||
|
||
scheduleInstanceId String?
|
||
scheduleId String?
|
||
|
||
sourceBulkActionItems BulkActionItem[] @relation("SourceActionItemRun")
|
||
destinationBulkActionItems BulkActionItem[] @relation("DestinationActionItemRun")
|
||
|
||
bulkActionGroupIds String[] @default([])
|
||
|
||
logsDeletedAt DateTime?
|
||
|
||
replayedFromTaskRunFriendlyId String?
|
||
|
||
/// This represents the original task that that was triggered outside of a Trigger.dev task
|
||
rootTaskRun TaskRun? @relation("TaskRootRun", fields: [rootTaskRunId], references: [id], onDelete: SetNull, onUpdate: NoAction)
|
||
rootTaskRunId String?
|
||
|
||
/// The root run will have a list of all the descendant runs, children, grand children, etc.
|
||
descendantRuns TaskRun[] @relation("TaskRootRun")
|
||
|
||
/// The immediate parent run of this task run
|
||
parentTaskRun TaskRun? @relation("TaskParentRun", fields: [parentTaskRunId], references: [id], onDelete: SetNull, onUpdate: NoAction)
|
||
parentTaskRunId String?
|
||
|
||
/// The immediate child runs of this task run
|
||
childRuns TaskRun[] @relation("TaskParentRun")
|
||
|
||
/// The immediate parent attempt of this task run
|
||
parentTaskRunAttempt TaskRunAttempt? @relation("TaskParentRunAttempt", fields: [parentTaskRunAttemptId], references: [id], onDelete: SetNull, onUpdate: NoAction)
|
||
parentTaskRunAttemptId String?
|
||
|
||
/// The batch run that this task run is a part of
|
||
batch BatchTaskRun? @relation(fields: [batchId], references: [id], onDelete: SetNull, onUpdate: NoAction)
|
||
batchId String?
|
||
|
||
/// whether or not the task run was created because of a triggerAndWait for batchTriggerAndWait
|
||
resumeParentOnCompletion Boolean @default(false)
|
||
|
||
/// The depth of this task run in the task run hierarchy
|
||
depth Int @default(0)
|
||
|
||
/// The span ID of the "trigger" span in the parent task run
|
||
parentSpanId String?
|
||
|
||
/// Holds the state of the run chain for deadlock detection
|
||
runChainState Json?
|
||
|
||
/// seed run metadata
|
||
seedMetadata String?
|
||
seedMetadataType String @default("application/json")
|
||
|
||
/// Run metadata
|
||
metadata String?
|
||
metadataType String @default("application/json")
|
||
metadataVersion Int @default(1)
|
||
|
||
/// Structured annotations: triggerSource, triggerAction, rootTriggerSource, rootScheduleId
|
||
annotations Json?
|
||
|
||
/// Whether the latest attempt was a warm start. Null until first attempt starts.
|
||
isWarmStart Boolean?
|
||
|
||
/// Run output
|
||
output String?
|
||
outputType String @default("application/json")
|
||
|
||
/// Run error
|
||
error Json?
|
||
|
||
/// Organization's billing plan type (cached for fallback when billing API fails)
|
||
planType String?
|
||
|
||
maxDurationInSeconds Int?
|
||
|
||
/// The version of the realtime streams implementation used by the run
|
||
realtimeStreamsVersion String @default("v1")
|
||
/// Store the stream keys that are being used by the run
|
||
realtimeStreams String[] @default([])
|
||
/// S2 basin where this run's realtime streams live. Stamped at create
|
||
/// time from `Organization.streamBasinName` so reads can resolve the
|
||
/// basin without joining org. Null when the org has no per-org basin
|
||
/// (OSS, or pre-backfill); reads fall back to the global basin.
|
||
streamBasinName String?
|
||
|
||
playgroundConversations PlaygroundConversation[]
|
||
|
||
@@unique([oneTimeUseToken])
|
||
@@unique([runtimeEnvironmentId, taskIdentifier, idempotencyKey])
|
||
// Finding child runs
|
||
@@index([parentTaskRunId])
|
||
// Run page inspector
|
||
@@index([spanId])
|
||
@@index([parentSpanId])
|
||
// Finding runs in a batch
|
||
@@index([runTags(ops: ArrayOps)], type: Gin)
|
||
@@index([runtimeEnvironmentId, batchId])
|
||
@@index([runtimeEnvironmentId, createdAt(sort: Desc)])
|
||
@@index([createdAt], type: Brin)
|
||
}
|
||
|
||
model TaskRunTemplate {
|
||
id String @id @default(cuid())
|
||
|
||
taskSlug String
|
||
triggerSource TaskTriggerSource
|
||
|
||
label String
|
||
|
||
payload String?
|
||
payloadType String @default("application/json")
|
||
metadata String?
|
||
metadataType String @default("application/json")
|
||
queue String?
|
||
concurrencyKey String?
|
||
ttlSeconds Int?
|
||
maxAttempts Int?
|
||
maxDurationSeconds Int?
|
||
tags String[]
|
||
machinePreset String?
|
||
|
||
projectId String
|
||
organizationId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([projectId, taskSlug, triggerSource, createdAt(sort: Desc)])
|
||
}
|
||
|
||
enum TaskRunStatus {
|
||
///
|
||
/// NON-FINAL STATUSES
|
||
///
|
||
|
||
/// Task has been scheduled to run in the future
|
||
DELAYED
|
||
/// Task is waiting to be executed by a worker
|
||
PENDING
|
||
|
||
/// The run is pending a version update because it cannot execute without additional information (task, queue, etc.). Replaces WAITING_FOR_DEPLOY
|
||
PENDING_VERSION
|
||
|
||
/// Task hasn't been deployed yet but is waiting to be executed. Deprecated in favor of PENDING_VERSION
|
||
WAITING_FOR_DEPLOY
|
||
|
||
/// Task has been dequeued from the queue but is not yet executing
|
||
DEQUEUED
|
||
|
||
/// Task is currently being executed by a worker
|
||
EXECUTING
|
||
|
||
/// Task has been paused by the system, and will be resumed by the system
|
||
WAITING_TO_RESUME
|
||
|
||
/// Task has failed and is waiting to be retried
|
||
RETRYING_AFTER_FAILURE
|
||
|
||
/// Task has been paused by the user, and can be resumed by the user
|
||
PAUSED
|
||
|
||
///
|
||
/// FINAL STATUSES
|
||
///
|
||
|
||
/// Task has been canceled by the user
|
||
CANCELED
|
||
|
||
/// Task was interrupted during execution, mostly this happens in development environments
|
||
INTERRUPTED
|
||
|
||
/// Task has been completed successfully
|
||
COMPLETED_SUCCESSFULLY
|
||
|
||
/// Task has been completed with errors
|
||
COMPLETED_WITH_ERRORS
|
||
|
||
/// Task has failed to complete, due to an error in the system
|
||
SYSTEM_FAILURE
|
||
|
||
/// Task has crashed and won't be retried, most likely the worker ran out of resources, e.g. memory or storage
|
||
CRASHED
|
||
|
||
/// Task reached the ttl without being executed
|
||
EXPIRED
|
||
|
||
/// Task has been timed out when using maxDuration
|
||
TIMED_OUT
|
||
}
|
||
|
||
enum RunEngineVersion {
|
||
/// The original version that uses marqs v1 and Graphile
|
||
V1
|
||
V2
|
||
}
|
||
|
||
/// Used by the RunEngine during TaskRun execution
|
||
/// It has the required information to transactionally progress a run through states,
|
||
/// and prevent side effects like heartbeats failing a run that has progressed.
|
||
/// It is optimised for performance and is designed to be cleared at some point,
|
||
/// so there are no cascading relationships to other models.
|
||
model TaskRunExecutionSnapshot {
|
||
id String @id @default(cuid())
|
||
|
||
/// This should always be 2+ (V1 didn't use the run engine or snapshots)
|
||
engine RunEngineVersion @default(V2)
|
||
|
||
/// The execution status
|
||
executionStatus TaskRunExecutionStatus
|
||
/// For debugging
|
||
description String
|
||
|
||
/// We store invalid snapshots as a record of the run state when we tried to move
|
||
isValid Boolean @default(true)
|
||
error String?
|
||
|
||
/// The previous snapshot ID
|
||
previousSnapshotId String?
|
||
|
||
/// Run
|
||
runId String
|
||
run TaskRun @relation(fields: [runId], references: [id])
|
||
runStatus TaskRunStatus
|
||
|
||
// Batch
|
||
batchId String?
|
||
|
||
/// This is the current run attempt number. Users can define how many attempts they want for a run.
|
||
attemptNumber Int?
|
||
|
||
/// Environment
|
||
environmentId String
|
||
environmentType RuntimeEnvironmentType
|
||
projectId String
|
||
organizationId String
|
||
|
||
/// Waitpoints that have been completed for this execution
|
||
completedWaitpoints Waitpoint[] @relation("completedWaitpoints")
|
||
|
||
/// An array of waitpoint IDs in the correct order, used for batches
|
||
completedWaitpointOrder String[]
|
||
|
||
/// Checkpoint
|
||
checkpointId String?
|
||
checkpoint TaskRunCheckpoint? @relation(fields: [checkpointId], references: [id])
|
||
|
||
/// Worker
|
||
workerId String?
|
||
runnerId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
lastHeartbeatAt DateTime?
|
||
|
||
/// Metadata used by various systems in the run engine
|
||
metadata Json?
|
||
|
||
/// Used to get the latest valid snapshot quickly
|
||
@@index([runId, isValid, createdAt(sort: Desc)])
|
||
}
|
||
|
||
enum TaskRunExecutionStatus {
|
||
/// Run has been created
|
||
RUN_CREATED
|
||
/// Run is delayed, waiting to be enqueued
|
||
DELAYED
|
||
/// Run is in the RunQueue
|
||
QUEUED
|
||
/// Run is in the RunQueue, and is also executing. This happens when a run is continued cannot reacquire concurrency
|
||
QUEUED_EXECUTING
|
||
/// Run has been pulled from the queue, but isn't executing yet
|
||
PENDING_EXECUTING
|
||
/// Run is executing on a worker
|
||
EXECUTING
|
||
/// Run is executing on a worker but is waiting for waitpoints to complete
|
||
EXECUTING_WITH_WAITPOINTS
|
||
/// Run has been suspended and may be waiting for waitpoints to complete before resuming
|
||
SUSPENDED
|
||
/// Run has been scheduled for cancellation
|
||
PENDING_CANCEL
|
||
/// Run is finished (success of failure)
|
||
FINISHED
|
||
}
|
||
|
||
model TaskRunCheckpoint {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
type TaskRunCheckpointType
|
||
location String
|
||
imageRef String?
|
||
reason String?
|
||
metadata String?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
executionSnapshot TaskRunExecutionSnapshot[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
enum TaskRunCheckpointType {
|
||
DOCKER
|
||
KUBERNETES
|
||
COMPUTE
|
||
}
|
||
|
||
/// A Waitpoint blocks a run from continuing until it's completed
|
||
/// If there's a waitpoint blocking a run, it shouldn't be in the queue
|
||
model Waitpoint {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
type WaitpointType
|
||
status WaitpointStatus @default(PENDING)
|
||
|
||
completedAt DateTime?
|
||
|
||
/// If it's an Event type waitpoint, this is the event. It can also be provided for the DATETIME type
|
||
idempotencyKey String
|
||
/// If this is true then we can show it in the dashboard/return it from the SDK
|
||
userProvidedIdempotencyKey Boolean
|
||
|
||
/// If there's a user provided idempotency key, this is the time it expires at
|
||
idempotencyKeyExpiresAt DateTime?
|
||
|
||
/// If an idempotencyKey is no longer active, we store it here and generate a new one for the idempotencyKey field.
|
||
/// Clearing an idempotencyKey is useful for debounce or cancelling child runs.
|
||
/// This is a workaround because Prisma doesn't support partial indexes.
|
||
inactiveIdempotencyKey String?
|
||
|
||
/// If it's a RUN type waitpoint, this is the associated run
|
||
completedByTaskRunId String? @unique
|
||
completedByTaskRun TaskRun? @relation("CompletingRun", fields: [completedByTaskRunId], references: [id], onDelete: SetNull)
|
||
|
||
/// If it's a DATETIME type waitpoint, this is the date.
|
||
/// If it's a MANUAL waitpoint, this can be set as the `timeout`.
|
||
completedAfter DateTime?
|
||
|
||
/// If it's a BATCH type waitpoint, this is the associated batch
|
||
completedByBatchId String?
|
||
completedByBatch BatchTaskRun? @relation(fields: [completedByBatchId], references: [id], onDelete: SetNull)
|
||
|
||
/// The runs this waitpoint is blocking
|
||
blockingTaskRuns TaskRunWaitpoint[]
|
||
|
||
/// All runs that have ever been blocked by this waitpoint, used for display purposes
|
||
connectedRuns TaskRun[] @relation("WaitpointRunConnections")
|
||
|
||
/// When a waitpoint is complete
|
||
completedExecutionSnapshots TaskRunExecutionSnapshot[] @relation("completedWaitpoints")
|
||
|
||
/// When completed, an output can be stored here
|
||
output String?
|
||
outputType String @default("application/json")
|
||
outputIsError Boolean @default(false)
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// Denormized column that holds the raw tags
|
||
/// Denormalized column that holds the raw tags
|
||
tags String[]
|
||
|
||
/// Quickly find an idempotent waitpoint
|
||
@@unique([environmentId, idempotencyKey])
|
||
/// Quickly find a batch waitpoint
|
||
@@index([completedByBatchId])
|
||
/// Used on the Waitpoint dashboard pages
|
||
/// Time period filtering
|
||
@@index([environmentId, type, createdAt(sort: Desc)])
|
||
/// Status filtering
|
||
@@index([environmentId, type, status])
|
||
/// For the waitpoint token dashboard page
|
||
@@index([environmentId, type, id(sort: Desc)])
|
||
}
|
||
|
||
enum WaitpointType {
|
||
RUN
|
||
DATETIME
|
||
MANUAL
|
||
BATCH
|
||
}
|
||
|
||
enum WaitpointStatus {
|
||
PENDING
|
||
COMPLETED
|
||
}
|
||
|
||
model TaskRunWaitpoint {
|
||
id String @id @default(cuid())
|
||
|
||
taskRun TaskRun @relation(fields: [taskRunId], references: [id])
|
||
taskRunId String
|
||
|
||
waitpoint Waitpoint @relation(fields: [waitpointId], references: [id])
|
||
waitpointId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
/// This span id is completed when the waitpoint is completed. This is used with cached runs (idempotent)
|
||
spanIdToComplete String?
|
||
|
||
//associated batch
|
||
batchId String?
|
||
batch BatchTaskRun? @relation(fields: [batchId], references: [id])
|
||
//if there's an associated batch and this isn't set it's for the entire batch
|
||
//if it is set, it's a specific run in the batch
|
||
batchIndex Int?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// There are two constraints, the one below and also one that Prisma doesn't support
|
||
/// The second one implemented in SQL only prevents a TaskRun + Waitpoint with a null batchIndex
|
||
@@unique([taskRunId, waitpointId, batchIndex])
|
||
@@index([taskRunId])
|
||
@@index([waitpointId])
|
||
}
|
||
|
||
model WaitpointTag {
|
||
id String @id @default(cuid())
|
||
name String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([environmentId, name])
|
||
}
|
||
|
||
model FeatureFlag {
|
||
id String @id @default(cuid())
|
||
|
||
key String @unique
|
||
value Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model WorkerInstance {
|
||
id String @id @default(cuid())
|
||
|
||
/// For example "worker-1"
|
||
name String
|
||
|
||
/// If managed, it will default to the name, e.g. "worker-1"
|
||
/// If unmanged, it will be prefixed with the deployment ID e.g. "deploy-123-worker-1"
|
||
resourceIdentifier String
|
||
|
||
metadata Json?
|
||
|
||
workerGroup WorkerInstanceGroup @relation(fields: [workerGroupId], references: [id])
|
||
workerGroupId String
|
||
|
||
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String?
|
||
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String?
|
||
|
||
environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String?
|
||
|
||
deployment WorkerDeployment? @relation(fields: [deploymentId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
deploymentId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
lastDequeueAt DateTime?
|
||
lastHeartbeatAt DateTime?
|
||
|
||
@@unique([workerGroupId, resourceIdentifier])
|
||
}
|
||
|
||
enum WorkerInstanceGroupType {
|
||
MANAGED
|
||
UNMANAGED
|
||
}
|
||
|
||
enum WorkloadType {
|
||
CONTAINER
|
||
MICROVM
|
||
}
|
||
|
||
model WorkerInstanceGroup {
|
||
id String @id @default(cuid())
|
||
type WorkerInstanceGroupType
|
||
|
||
/// For example "us-east-1"
|
||
name String
|
||
|
||
/// If managed, it will default to the name, e.g. "us-east-1"
|
||
/// If unmanged, it will be prefixed with the project ID e.g. "project_1-us-east-1"
|
||
masterQueue String @unique
|
||
|
||
/// "N. Virginia, USA", "Frankfurt, Germany", etc. Used for display purposes
|
||
description String?
|
||
hidden Boolean @default(false)
|
||
|
||
token WorkerGroupToken @relation(fields: [tokenId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
tokenId String @unique
|
||
|
||
workers WorkerInstance[]
|
||
backgroundWorkers BackgroundWorker[]
|
||
|
||
defaultForProjects Project[] @relation("ProjectDefaultWorkerGroup")
|
||
|
||
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String?
|
||
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String?
|
||
|
||
cloudProvider String?
|
||
/// "usa", "europe", etc. Used like a pseudo enum for things like flags
|
||
location String?
|
||
staticIPs String?
|
||
|
||
workloadType WorkloadType @default(CONTAINER)
|
||
|
||
/// Geo region; container + MICROVM groups for one geo share it. Set-once once it has runs.
|
||
region String?
|
||
|
||
/// When true, runs enqueued to this worker queue may skip the intermediate queue
|
||
/// and be pushed directly to the worker queue when concurrency is available.
|
||
enableFastPath Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model WorkerGroupToken {
|
||
id String @id @default(cuid())
|
||
|
||
tokenHash String @unique
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
workerGroup WorkerInstanceGroup?
|
||
}
|
||
|
||
model TaskRunTag {
|
||
id String @id @default(cuid())
|
||
name String
|
||
|
||
friendlyId String @unique
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([projectId, name])
|
||
//Makes run filtering by tag faster
|
||
@@index([name, id])
|
||
}
|
||
|
||
/// This is used for triggerAndWait and batchTriggerAndWait. The taskRun is the child task, it points at a parent attempt or a batch
|
||
model TaskRunDependency {
|
||
id String @id @default(cuid())
|
||
|
||
/// The child run
|
||
taskRun TaskRun @relation(fields: [taskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
taskRunId String @unique
|
||
|
||
checkpointEvent CheckpointRestoreEvent? @relation(fields: [checkpointEventId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
checkpointEventId String? @unique
|
||
|
||
/// An attempt that is dependent on this task run.
|
||
dependentAttempt TaskRunAttempt? @relation(fields: [dependentAttemptId], references: [id])
|
||
dependentAttemptId String?
|
||
|
||
/// A batch run that is dependent on this task run
|
||
dependentBatchRun BatchTaskRun? @relation("dependentBatchRun", fields: [dependentBatchRunId], references: [id])
|
||
dependentBatchRunId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
resumedAt DateTime?
|
||
|
||
@@index([dependentAttemptId])
|
||
@@index([dependentBatchRunId])
|
||
}
|
||
|
||
/// deprecated, we hadn't included the project id in the unique constraint
|
||
model TaskRunCounter {
|
||
taskIdentifier String @id
|
||
lastNumber Int @default(0)
|
||
}
|
||
|
||
/// Used for the TaskRun number (e.g. #1,421)
|
||
model TaskRunNumberCounter {
|
||
id String @id @default(cuid())
|
||
|
||
taskIdentifier String
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
lastNumber Int @default(0)
|
||
|
||
@@unique([taskIdentifier, environmentId])
|
||
}
|
||
|
||
/// This is not used from engine v2+, attempts use the TaskRunExecutionSnapshot and TaskRun
|
||
model TaskRunAttempt {
|
||
id String @id @default(cuid())
|
||
number Int @default(0)
|
||
|
||
friendlyId String @unique
|
||
|
||
taskRun TaskRun @relation("attempts", fields: [taskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
taskRunId String
|
||
|
||
backgroundWorker BackgroundWorker @relation(fields: [backgroundWorkerId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
backgroundWorkerId String
|
||
|
||
backgroundWorkerTask BackgroundWorkerTask @relation(fields: [backgroundWorkerTaskId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
backgroundWorkerTaskId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
queue TaskQueue @relation(fields: [queueId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
queueId String
|
||
|
||
status TaskRunAttemptStatus @default(PENDING)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
startedAt DateTime?
|
||
completedAt DateTime?
|
||
|
||
usageDurationMs Int @default(0)
|
||
|
||
error Json?
|
||
output String?
|
||
outputType String @default("application/json")
|
||
|
||
dependencies TaskRunDependency[]
|
||
batchDependencies BatchTaskRun[]
|
||
|
||
checkpoints Checkpoint[]
|
||
batchTaskRunItems BatchTaskRunItem[]
|
||
CheckpointRestoreEvent CheckpointRestoreEvent[]
|
||
childRuns TaskRun[] @relation("TaskParentRunAttempt")
|
||
|
||
@@unique([taskRunId, number])
|
||
@@index([taskRunId])
|
||
}
|
||
|
||
enum TaskRunAttemptStatus {
|
||
/// NON-FINAL
|
||
PENDING
|
||
EXECUTING
|
||
PAUSED
|
||
/// FINAL
|
||
FAILED
|
||
CANCELED
|
||
COMPLETED
|
||
}
|
||
|
||
/// This is the unified otel span/log model that will eventually be replaced by clickhouse
|
||
model TaskEvent {
|
||
id String @id @default(cuid())
|
||
|
||
/// This matches the span name for a trace event, or the log body for a log event
|
||
message String
|
||
|
||
traceId String
|
||
spanId String
|
||
parentId String?
|
||
tracestate String?
|
||
|
||
isError Boolean @default(false)
|
||
isPartial Boolean @default(false)
|
||
isCancelled Boolean @default(false)
|
||
/// deprecated: don't use this, moving this to properties, this now uses TaskEventKind.LOG
|
||
isDebug Boolean @default(false)
|
||
|
||
serviceName String
|
||
serviceNamespace String
|
||
|
||
level TaskEventLevel @default(TRACE)
|
||
kind TaskEventKind @default(INTERNAL)
|
||
status TaskEventStatus @default(UNSET)
|
||
|
||
links Json?
|
||
events Json?
|
||
|
||
/// This is the time the event started in nanoseconds since the epoch
|
||
startTime BigInt
|
||
|
||
/// This is the duration of the event in nanoseconds
|
||
duration BigInt @default(0)
|
||
|
||
attemptId String?
|
||
attemptNumber Int?
|
||
|
||
environmentId String
|
||
environmentType RuntimeEnvironmentType
|
||
|
||
organizationId String
|
||
|
||
projectId String
|
||
projectRef String
|
||
|
||
runId String
|
||
runIsTest Boolean @default(false)
|
||
|
||
idempotencyKey String?
|
||
|
||
taskSlug String
|
||
taskPath String?
|
||
taskExportName String?
|
||
|
||
workerId String?
|
||
workerVersion String?
|
||
|
||
queueId String?
|
||
queueName String?
|
||
|
||
batchId String?
|
||
|
||
/// This represents all the span attributes available, like http.status_code, and special attributes like $style.icon, $output, $metadata.payload.userId, as it's used for searching and filtering
|
||
properties Json
|
||
|
||
/// This represents all span attributes in the $metadata namespace, like $metadata.payload
|
||
metadata Json?
|
||
|
||
/// This represents all span attributes in the $style namespace, like $style
|
||
style Json?
|
||
|
||
/// This represents all span attributes in the $output namespace, like $output
|
||
output Json?
|
||
|
||
/// This represents the mimetype of the output, such as application/json or application/super+json
|
||
outputType String?
|
||
|
||
payload Json?
|
||
payloadType String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
// This represents the amount of "usage time" the event took, e.g. the CPU time
|
||
usageDurationMs Int @default(0)
|
||
usageCostInCents Float @default(0)
|
||
|
||
machinePreset String?
|
||
machinePresetCpu Float?
|
||
machinePresetMemory Float?
|
||
machinePresetCentsPerMs Float?
|
||
|
||
/// Used on the run page
|
||
@@index([traceId])
|
||
/// Used when looking up span events to complete when a run completes
|
||
@@index([spanId])
|
||
// Used for getting all logs for a run
|
||
@@index([runId])
|
||
}
|
||
|
||
enum TaskEventLevel {
|
||
TRACE
|
||
DEBUG
|
||
LOG
|
||
INFO
|
||
WARN
|
||
ERROR
|
||
}
|
||
|
||
enum TaskEventKind {
|
||
UNSPECIFIED
|
||
INTERNAL
|
||
SERVER
|
||
CLIENT
|
||
PRODUCER
|
||
CONSUMER
|
||
UNRECOGNIZED
|
||
LOG
|
||
}
|
||
|
||
enum TaskEventStatus {
|
||
UNSET
|
||
OK
|
||
ERROR
|
||
UNRECOGNIZED
|
||
}
|
||
|
||
model TaskQueue {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
name String
|
||
type TaskQueueType @default(VIRTUAL)
|
||
|
||
version TaskQueueVersion @default(V1)
|
||
orderableName String?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
/// Represents the current concurrency limit for the queue
|
||
concurrencyLimit Int?
|
||
/// When the concurrency limit was overridden
|
||
concurrencyLimitOverriddenAt DateTime?
|
||
/// Who overrode the concurrency limit (will be null if overridden via the API)
|
||
concurrencyLimitOverriddenBy String?
|
||
/// If concurrencyLimit is overridden, this is the overridden value
|
||
concurrencyLimitBase Int?
|
||
/// If the override was expressed as a percentage of the environment limit, this stores that
|
||
/// percentage (the source of truth). The absolute concurrencyLimit is materialized from it.
|
||
/// Decimal(5,2) allows fractional percentages like 12.50% (0.01–100.00).
|
||
concurrencyLimitOverridePercent Decimal? @db.Decimal(5, 2)
|
||
rateLimit Json?
|
||
|
||
paused Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
attempts TaskRunAttempt[]
|
||
tasks BackgroundWorkerTask[]
|
||
workers BackgroundWorker[]
|
||
|
||
@@unique([runtimeEnvironmentId, name])
|
||
}
|
||
|
||
enum TaskQueueType {
|
||
VIRTUAL
|
||
NAMED
|
||
}
|
||
|
||
enum TaskQueueVersion {
|
||
V1
|
||
V2
|
||
}
|
||
|
||
model BatchTaskRun {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique
|
||
idempotencyKey String?
|
||
idempotencyKeyExpiresAt DateTime?
|
||
status BatchTaskRunStatus @default(PENDING)
|
||
runtimeEnvironmentId String
|
||
/// This only includes new runs, not idempotent runs.
|
||
runs TaskRun[]
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// new columns
|
||
/// Friendly IDs
|
||
runIds String[] @default([])
|
||
runCount Int @default(0)
|
||
payload String?
|
||
payloadType String @default("application/json")
|
||
options Json?
|
||
batchVersion String @default("v1")
|
||
|
||
//engine v2
|
||
/// Specific run blockers,
|
||
runsBlocked TaskRunWaitpoint[]
|
||
/// Waitpoints that are blocked by this batch.
|
||
/// When a Batch is created it blocks execution of the associated parent run (for andWait)
|
||
waitpoints Waitpoint[]
|
||
|
||
// This is for v3 batches
|
||
/// sealed is set to true once no more items can be added to the batch
|
||
sealed Boolean @default(false)
|
||
sealedAt DateTime?
|
||
/// this is the expected number of items in the batch
|
||
expectedCount Int @default(0)
|
||
/// this is the completed number of items in the batch. once this reaches expectedCount, and the batch is sealed, the batch is considered completed
|
||
completedCount Int @default(0)
|
||
completedAt DateTime?
|
||
resumedAt DateTime?
|
||
|
||
/// this is used to be able to "seal" this BatchTaskRun when all of the runs have been triggered asynchronously, and using the "parallel" processing strategy
|
||
processingJobsCount Int @default(0)
|
||
processingJobsExpectedCount Int @default(0)
|
||
|
||
/// optional token that can be used to authenticate the task run
|
||
oneTimeUseToken String?
|
||
|
||
// Run Engine v2 batch queue fields
|
||
/// When processing started (status changed to PROCESSING)
|
||
processingStartedAt DateTime?
|
||
/// When processing completed (all items processed)
|
||
processingCompletedAt DateTime?
|
||
/// Count of successfully created runs
|
||
successfulRunCount Int?
|
||
/// Count of failed run creations
|
||
failedRunCount Int?
|
||
/// Detailed failure records
|
||
errors BatchTaskRunError[]
|
||
|
||
///all the below properties are engine v1 only
|
||
items BatchTaskRunItem[]
|
||
taskIdentifier String?
|
||
checkpointEvent CheckpointRestoreEvent? @relation(fields: [checkpointEventId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
checkpointEventId String? @unique
|
||
dependentTaskAttempt TaskRunAttempt? @relation(fields: [dependentTaskAttemptId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
dependentTaskAttemptId String?
|
||
runDependencies TaskRunDependency[] @relation("dependentBatchRun")
|
||
|
||
@@unique([oneTimeUseToken])
|
||
///this is used for all engine versions
|
||
@@unique([runtimeEnvironmentId, idempotencyKey])
|
||
@@index([dependentTaskAttemptId])
|
||
// This is for the batch list dashboard page
|
||
@@index([runtimeEnvironmentId, id(sort: Desc)])
|
||
@@index([runtimeEnvironmentId, createdAt(sort: Desc), id(sort: Desc)])
|
||
}
|
||
|
||
enum BatchTaskRunStatus {
|
||
PENDING
|
||
PROCESSING
|
||
COMPLETED
|
||
PARTIAL_FAILED
|
||
ABORTED
|
||
}
|
||
|
||
///Used in engine V1 only
|
||
model BatchTaskRunItem {
|
||
id String @id @default(cuid())
|
||
|
||
status BatchTaskRunItemStatus @default(PENDING)
|
||
|
||
batchTaskRun BatchTaskRun @relation(fields: [batchTaskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
batchTaskRunId String
|
||
|
||
taskRun TaskRun @relation(fields: [taskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
taskRunId String
|
||
|
||
taskRunAttempt TaskRunAttempt? @relation(fields: [taskRunAttemptId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
taskRunAttemptId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
completedAt DateTime?
|
||
|
||
@@unique([batchTaskRunId, taskRunId])
|
||
@@index([taskRunAttemptId], map: "idx_batchtaskrunitem_taskrunattempt")
|
||
@@index([taskRunId], map: "idx_batchtaskrunitem_taskrun")
|
||
}
|
||
|
||
enum BatchTaskRunItemStatus {
|
||
PENDING
|
||
FAILED
|
||
CANCELED
|
||
COMPLETED
|
||
}
|
||
|
||
/// Track individual run creation failures in batch processing (Run Engine v2)
|
||
model BatchTaskRunError {
|
||
id String @id @default(cuid())
|
||
batchTaskRun BatchTaskRun @relation(fields: [batchTaskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
batchTaskRunId String
|
||
|
||
/// Which item in the batch (0-based index)
|
||
index Int
|
||
/// The task identifier that was being triggered
|
||
taskIdentifier String
|
||
/// The payload that failed (JSON, may be truncated)
|
||
payload String?
|
||
/// The options that were used
|
||
options Json?
|
||
/// Error message
|
||
error String
|
||
/// Error code if available
|
||
errorCode String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@unique([batchTaskRunId, index])
|
||
@@index([batchTaskRunId])
|
||
}
|
||
|
||
model EnvironmentVariable {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique
|
||
key String
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
values EnvironmentVariableValue[]
|
||
|
||
@@unique([projectId, key])
|
||
}
|
||
|
||
model EnvironmentVariableValue {
|
||
id String @id @default(cuid())
|
||
valueReference SecretReference? @relation(fields: [valueReferenceId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
valueReferenceId String?
|
||
variable EnvironmentVariable @relation(fields: [variableId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
variableId String
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
/// If true, the value is secret and cannot be revealed
|
||
isSecret Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
version Int @default(1)
|
||
lastUpdatedBy Json?
|
||
|
||
@@unique([variableId, environmentId])
|
||
@@index([environmentId])
|
||
@@index([valueReferenceId])
|
||
}
|
||
|
||
model Checkpoint {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
type CheckpointType
|
||
location String
|
||
imageRef String
|
||
reason String?
|
||
metadata String?
|
||
|
||
events CheckpointRestoreEvent[]
|
||
|
||
run TaskRun @relation(fields: [runId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runId String
|
||
|
||
attempt TaskRunAttempt @relation(fields: [attemptId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
attemptId String
|
||
attemptNumber Int?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([attemptId])
|
||
@@index([runId])
|
||
}
|
||
|
||
enum CheckpointType {
|
||
DOCKER
|
||
KUBERNETES
|
||
}
|
||
|
||
model CheckpointRestoreEvent {
|
||
id String @id @default(cuid())
|
||
|
||
type CheckpointRestoreEventType
|
||
reason String?
|
||
metadata String?
|
||
|
||
checkpoint Checkpoint @relation(fields: [checkpointId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
checkpointId String
|
||
|
||
run TaskRun @relation(fields: [runId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runId String
|
||
|
||
attempt TaskRunAttempt @relation(fields: [attemptId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
attemptId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
taskRunDependency TaskRunDependency?
|
||
batchTaskRunDependency BatchTaskRun?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([checkpointId])
|
||
@@index([runId])
|
||
}
|
||
|
||
enum CheckpointRestoreEventType {
|
||
CHECKPOINT
|
||
RESTORE
|
||
}
|
||
|
||
enum WorkerDeploymentType {
|
||
MANAGED
|
||
UNMANAGED
|
||
V1
|
||
}
|
||
|
||
model WorkerDeployment {
|
||
id String @id @default(cuid())
|
||
|
||
contentHash String
|
||
friendlyId String @unique
|
||
shortCode String
|
||
version String
|
||
|
||
runtime String?
|
||
runtimeVersion String?
|
||
|
||
imageReference String?
|
||
imagePlatform String @default("linux/amd64")
|
||
|
||
externalBuildData Json?
|
||
buildServerMetadata Json?
|
||
|
||
status WorkerDeploymentStatus @default(PENDING)
|
||
type WorkerDeploymentType @default(V1)
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
worker BackgroundWorker? @relation(fields: [workerId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
workerId String? @unique
|
||
|
||
triggeredBy User? @relation(fields: [triggeredById], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
triggeredById String?
|
||
triggeredVia String?
|
||
commitSHA String?
|
||
externalId String?
|
||
|
||
startedAt DateTime?
|
||
installedAt DateTime?
|
||
builtAt DateTime?
|
||
deployedAt DateTime?
|
||
|
||
failedAt DateTime?
|
||
errorData Json?
|
||
|
||
canceledAt DateTime?
|
||
canceledReason String?
|
||
|
||
// This is GitMeta type
|
||
git Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
promotions WorkerDeploymentPromotion[]
|
||
alerts ProjectAlert[]
|
||
workerInstance WorkerInstance[]
|
||
integrationDeployments IntegrationDeployment[]
|
||
|
||
@@unique([projectId, shortCode])
|
||
@@unique([environmentId, version])
|
||
@@index([commitSHA])
|
||
@@index([environmentId, createdAt])
|
||
@@index([environmentId, status, id])
|
||
@@index([environmentId, externalId])
|
||
}
|
||
|
||
enum WorkerDeploymentStatus {
|
||
PENDING
|
||
INSTALLING
|
||
/// This is the status when the image is being built
|
||
BUILDING
|
||
/// This is the status when the image is built and we are waiting for the indexing to finish
|
||
DEPLOYING
|
||
/// This is the status when the image is built and indexed, meaning we have everything we need to deploy
|
||
DEPLOYED
|
||
FAILED
|
||
CANCELED
|
||
/// This is the status when the image is built and indexing does not finish in time
|
||
TIMED_OUT
|
||
}
|
||
|
||
model WorkerDeploymentPromotion {
|
||
id String @id @default(cuid())
|
||
|
||
/// This is the promotion label, e.g. "current"
|
||
label String
|
||
|
||
deployment WorkerDeployment @relation(fields: [deploymentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
deploymentId String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
// Only one promotion per environment can be active at a time
|
||
@@unique([environmentId, label])
|
||
}
|
||
|
||
///Schedules can be attached to tasks to trigger them on a schedule
|
||
model TaskSchedule {
|
||
id String @id @default(cuid())
|
||
|
||
type ScheduleType @default(IMPERATIVE)
|
||
|
||
///users see this as `id`. They start with schedule_
|
||
friendlyId String @unique
|
||
///a reference to a task (not a foreign key because it's across versions)
|
||
taskIdentifier String
|
||
|
||
///can be provided and we won't create another with the same key
|
||
deduplicationKey String @default(cuid())
|
||
userProvidedDeduplicationKey Boolean @default(false)
|
||
|
||
///the CRON pattern
|
||
generatorExpression String
|
||
generatorDescription String @default("")
|
||
generatorType ScheduleGeneratorType @default(CRON)
|
||
|
||
/// These are IANA format string, or the default "UTC". E.g. "America/New_York"
|
||
timezone String @default("UTC")
|
||
|
||
// Cron spread
|
||
windowDurationSeconds Int?
|
||
windowPercentage Int?
|
||
|
||
///Can be provided by the user then accessed inside a run
|
||
externalId String?
|
||
|
||
///Instances of the schedule that are active
|
||
instances TaskScheduleInstance[]
|
||
|
||
/// @deprecated stop writing 2026-04-30; reads moved out of code (UI now derives from cron's previous slot). Drop in follow-up.
|
||
lastRunTriggeredAt DateTime?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
active Boolean @default(true)
|
||
|
||
@@unique([projectId, deduplicationKey])
|
||
/// Dashboard list view
|
||
@@index([projectId])
|
||
@@index([projectId, createdAt(sort: Desc)])
|
||
}
|
||
|
||
enum ScheduleType {
|
||
/// defined on your task using the `cron` property
|
||
DECLARATIVE
|
||
/// explicit calls to the SDK are used to create, or using the dashboard
|
||
IMPERATIVE
|
||
}
|
||
|
||
enum ScheduleGeneratorType {
|
||
CRON
|
||
}
|
||
|
||
///An instance links a schedule with an environment
|
||
model TaskScheduleInstance {
|
||
id String @id @default(cuid())
|
||
|
||
taskSchedule TaskSchedule @relation(fields: [taskScheduleId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
taskScheduleId String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
// Durable cron spread phase
|
||
schedulePhase Int?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
active Boolean @default(true)
|
||
|
||
/// @deprecated stop writing 2026-04-30; engine derives from cron + exactScheduleTime. Drop in follow-up.
|
||
lastScheduledTimestamp DateTime?
|
||
/// @deprecated stop writing 2026-04-30; engine derives from cron + now(). Drop in follow-up.
|
||
nextScheduledTimestamp DateTime?
|
||
|
||
//you can only have a schedule attached to each environment once
|
||
@@unique([taskScheduleId, environmentId])
|
||
@@index([environmentId])
|
||
@@index([projectId, active])
|
||
}
|
||
|
||
model RuntimeEnvironmentSession {
|
||
id String @id @default(cuid())
|
||
|
||
ipAddress String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
disconnectedAt DateTime?
|
||
|
||
currentEnvironments RuntimeEnvironment[] @relation("currentSession")
|
||
}
|
||
|
||
model ProjectAlertChannel {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
///can be provided and we won't create another with the same key
|
||
deduplicationKey String @default(cuid())
|
||
userProvidedDeduplicationKey Boolean @default(false)
|
||
|
||
integration OrganizationIntegration? @relation(fields: [integrationId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
integrationId String?
|
||
|
||
enabled Boolean @default(true)
|
||
|
||
type ProjectAlertChannelType
|
||
name String
|
||
properties Json
|
||
alertTypes ProjectAlertType[]
|
||
environmentTypes RuntimeEnvironmentType[] @default([STAGING, PRODUCTION])
|
||
|
||
errorAlertConfig Json?
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
alerts ProjectAlert[]
|
||
alertStorages ProjectAlertStorage[]
|
||
|
||
@@unique([projectId, deduplicationKey])
|
||
}
|
||
|
||
enum ProjectAlertChannelType {
|
||
EMAIL
|
||
SLACK
|
||
WEBHOOK
|
||
}
|
||
|
||
model ProjectAlert {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String
|
||
|
||
channel ProjectAlertChannel @relation(fields: [channelId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
channelId String
|
||
|
||
status ProjectAlertStatus @default(PENDING)
|
||
|
||
type ProjectAlertType
|
||
|
||
// Run-ops split: these reference run-subgraph rows that live on the dedicated run-ops DB for run-ops id
|
||
// runs, so the cross-DB FK can't hold. Scalar-only; the run is resolved via runStore.findRun.
|
||
taskRunAttemptId String?
|
||
|
||
taskRunId String?
|
||
|
||
workerDeployment WorkerDeployment? @relation(fields: [workerDeploymentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
workerDeploymentId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([channelId])
|
||
}
|
||
|
||
enum ProjectAlertType {
|
||
TASK_RUN
|
||
/// deprecated, we don't send new alerts for this type
|
||
TASK_RUN_ATTEMPT
|
||
DEPLOYMENT_FAILURE
|
||
DEPLOYMENT_SUCCESS
|
||
ERROR_GROUP
|
||
DASHBOARD_AGENT_WATCH
|
||
}
|
||
|
||
enum ProjectAlertStatus {
|
||
PENDING
|
||
SENT
|
||
FAILED
|
||
}
|
||
|
||
model ProjectAlertStorage {
|
||
id String @id @default(cuid())
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
alertChannel ProjectAlertChannel @relation(fields: [alertChannelId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
alertChannelId String
|
||
|
||
alertType ProjectAlertType
|
||
|
||
storageId String
|
||
storageData Json
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([alertChannelId, alertType, storageId])
|
||
}
|
||
|
||
model OrganizationIntegration {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
service IntegrationService
|
||
externalOrganizationId String? /// Identifier for external, integration's organization (e.g. Vercel's team)
|
||
|
||
integrationData Json
|
||
|
||
tokenReference SecretReference @relation(fields: [tokenReferenceId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
tokenReferenceId String
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
alertChannels ProjectAlertChannel[]
|
||
organizationProjectIntegration OrganizationProjectIntegration[]
|
||
|
||
@@index([externalOrganizationId])
|
||
}
|
||
|
||
model OrganizationProjectIntegration {
|
||
id String @id @default(cuid())
|
||
|
||
organizationIntegration OrganizationIntegration @relation(fields: [organizationIntegrationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationIntegrationId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
externalEntityId String /// Identifier for webhooks, for example Vercel's projectId
|
||
integrationData Json /// Save useful data like config or external entity name
|
||
installedBy String? /// UserId who installed the integration
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
@@index([projectId])
|
||
@@index([projectId, organizationIntegrationId])
|
||
@@index([externalEntityId])
|
||
}
|
||
|
||
enum IntegrationService {
|
||
SLACK
|
||
VERCEL
|
||
}
|
||
|
||
/// Bulk actions, like canceling and replaying runs
|
||
model BulkActionGroup {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
/// If this is set then it's a V2 Bulk Action that supports queries
|
||
environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String?
|
||
|
||
type BulkActionType
|
||
|
||
/// When the group is created it's pending. After we've processed all the items it's completed. This does not mean the associated runs are completed.
|
||
status BulkActionStatus @default(PENDING)
|
||
|
||
/// The query that will be executed to get the runs to process (if null, we are passing run ids directly)
|
||
queryName String?
|
||
/// The params that will be passed to the query
|
||
params Json?
|
||
/// Billing-limit dedupe key (grace hitAt or resolve job key). Null for dashboard bulk actions.
|
||
dedupeKey String?
|
||
/// The cursor that will be passed to the query (null for the first page)
|
||
cursor Json?
|
||
/// The number of runs that have been processed successfully
|
||
successCount Int @default(0)
|
||
/// The number of runs that have been failed
|
||
failureCount Int @default(0)
|
||
/// The total number of runs that will be processed
|
||
totalCount Int @default(0)
|
||
|
||
completionNotification BulkActionNotificationType @default(NONE)
|
||
|
||
/// The userId who did the bulk action
|
||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
userId String?
|
||
|
||
/// The name of the bulk action
|
||
name String?
|
||
|
||
/// The time the bulk action was completed
|
||
completedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([environmentId, createdAt(sort: Desc)])
|
||
// NOTE: the real index is PARTIAL (`WHERE "dedupeKey" IS NOT NULL`) and created
|
||
// CONCURRENTLY — see migration 20260626120001_bulk_action_group_dedupe_key_index.
|
||
// Prisma can't express partial indexes, so this @@index is an approximation (as with
|
||
// the INCLUDE/fillfactor indexes elsewhere in this schema). `migrate dev` will report
|
||
// drift here; do NOT accept its drop/recreate — keep the hand-written migration.
|
||
@@index([environmentId, type, dedupeKey])
|
||
// Backs the per-environment concurrent-replay limit (count of PENDING REPLAY groups).
|
||
// Plain composite (not partial) so Prisma's parameterized count reliably uses it; the
|
||
// migration creates it CONCURRENTLY to avoid locking writes. See migration
|
||
// 20260702120000_bulk_action_group_pending_replay_index.
|
||
@@index([environmentId, status, type])
|
||
}
|
||
|
||
enum BulkActionType {
|
||
/// Cancels existing runs. This populates the destination runs.
|
||
CANCEL
|
||
/// Replays existing runs. The original runs go as source runs, and the new runs go as destination runs.
|
||
REPLAY
|
||
}
|
||
|
||
enum BulkActionStatus {
|
||
PENDING
|
||
COMPLETED
|
||
ABORTED
|
||
}
|
||
|
||
enum BulkActionNotificationType {
|
||
NONE
|
||
EMAIL
|
||
}
|
||
|
||
model BulkActionItem {
|
||
id String @id @default(cuid())
|
||
|
||
/// @deprecated not used in new BulkActions
|
||
friendlyId String?
|
||
|
||
groupId String
|
||
|
||
type BulkActionType
|
||
|
||
/// When the item is created it's pending. After we've processed the item it's completed. This does not mean the associated runs are completed.
|
||
status BulkActionItemStatus @default(PENDING)
|
||
|
||
/// The run that is the source of the action, e.g. when replaying this is the original run
|
||
sourceRun TaskRun @relation("SourceActionItemRun", fields: [sourceRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
sourceRunId String
|
||
|
||
/// The run that's a result of the action, this will be set when the run has been created
|
||
destinationRun TaskRun? @relation("DestinationActionItemRun", fields: [destinationRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
destinationRunId String?
|
||
|
||
error String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
enum BulkActionItemStatus {
|
||
PENDING
|
||
COMPLETED
|
||
FAILED
|
||
}
|
||
|
||
model RealtimeStreamChunk {
|
||
id String @id @default(cuid())
|
||
|
||
key String
|
||
value String
|
||
|
||
sequence Int
|
||
|
||
runId String
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([runId])
|
||
@@index([createdAt])
|
||
}
|
||
|
||
/// This is the unified otel span/log model that will eventually be replaced by clickhouse
|
||
model TaskEventPartitioned {
|
||
id String @default(cuid())
|
||
/// This matches the span name for a trace event, or the log body for a log event
|
||
message String
|
||
|
||
traceId String
|
||
spanId String
|
||
parentId String?
|
||
tracestate String?
|
||
|
||
isError Boolean @default(false)
|
||
isPartial Boolean @default(false)
|
||
isCancelled Boolean @default(false)
|
||
|
||
serviceName String
|
||
serviceNamespace String
|
||
|
||
level TaskEventLevel @default(TRACE)
|
||
kind TaskEventKind @default(INTERNAL)
|
||
status TaskEventStatus @default(UNSET)
|
||
|
||
links Json?
|
||
events Json?
|
||
|
||
/// This is the time the event started in nanoseconds since the epoch
|
||
startTime BigInt
|
||
|
||
/// This is the duration of the event in nanoseconds
|
||
duration BigInt @default(0)
|
||
|
||
attemptId String?
|
||
attemptNumber Int?
|
||
|
||
environmentId String
|
||
environmentType RuntimeEnvironmentType
|
||
|
||
organizationId String
|
||
|
||
projectId String
|
||
projectRef String
|
||
|
||
runId String
|
||
runIsTest Boolean @default(false)
|
||
|
||
idempotencyKey String?
|
||
|
||
taskSlug String
|
||
taskPath String?
|
||
taskExportName String?
|
||
|
||
workerId String?
|
||
workerVersion String?
|
||
|
||
queueId String?
|
||
queueName String?
|
||
|
||
batchId String?
|
||
|
||
/// This represents all the span attributes available, like http.status_code, and special attributes like $style.icon, $output, $metadata.payload.userId, as it's used for searching and filtering
|
||
properties Json
|
||
|
||
/// This represents all span attributes in the $metadata namespace, like $metadata.payload
|
||
metadata Json?
|
||
|
||
/// This represents all span attributes in the $style namespace, like $style
|
||
style Json?
|
||
|
||
/// This represents all span attributes in the $output namespace, like $output
|
||
output Json?
|
||
|
||
/// This represents the mimetype of the output, such as application/json or application/super+json
|
||
outputType String?
|
||
|
||
payload Json?
|
||
payloadType String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
// This represents the amount of "usage time" the event took, e.g. the CPU time
|
||
usageDurationMs Int @default(0)
|
||
usageCostInCents Float @default(0)
|
||
|
||
machinePreset String?
|
||
machinePresetCpu Float?
|
||
machinePresetMemory Float?
|
||
machinePresetCentsPerMs Float?
|
||
|
||
@@id([id, createdAt])
|
||
/// Used on the run page
|
||
@@index([traceId])
|
||
/// Used when looking up span events to complete when a run completes
|
||
@@index([spanId])
|
||
// Used for getting all logs for a run
|
||
@@index([runId])
|
||
}
|
||
|
||
enum GithubRepositorySelection {
|
||
ALL
|
||
SELECTED
|
||
}
|
||
|
||
model GithubAppInstallation {
|
||
id String @id @default(cuid())
|
||
|
||
appInstallationId BigInt @unique
|
||
targetId BigInt
|
||
targetType String
|
||
accountHandle String
|
||
permissions Json?
|
||
repositorySelection GithubRepositorySelection
|
||
installedBy String?
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id])
|
||
organizationId String
|
||
|
||
repositories GithubRepository[]
|
||
|
||
deletedAt DateTime?
|
||
suspendedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([organizationId])
|
||
}
|
||
|
||
model GithubRepository {
|
||
id String @id @default(cuid())
|
||
|
||
githubId BigInt
|
||
name String
|
||
fullName String
|
||
htmlUrl String
|
||
private Boolean
|
||
defaultBranch String
|
||
|
||
installation GithubAppInstallation @relation(fields: [installationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
installationId String
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
ConnectedGithubRepository ConnectedGithubRepository[]
|
||
|
||
@@unique([installationId, githubId])
|
||
@@index([installationId])
|
||
}
|
||
|
||
model ConnectedGithubRepository {
|
||
id String @id @default(cuid())
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
repository GithubRepository @relation(fields: [repositoryId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
repositoryId String
|
||
|
||
// Branch configuration by environment slug, stored as JSON
|
||
// Example: {
|
||
// "prod": { "branch": "main" },
|
||
// "staging": { "branch": "staging" },
|
||
// }
|
||
// We could alternatively store tracking branch configurations in a separate table and reference the runtime environments properly.
|
||
// We don't need that functionality currently so it's simpler to store it here.
|
||
// Should we need to introduce such functionality in the future, e.g., supporting custom environments, the migration should be straightforward.
|
||
branchTracking Json
|
||
|
||
previewDeploymentsEnabled Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
// A project can only be connected to one repository
|
||
@@unique([projectId])
|
||
@@index([repositoryId])
|
||
}
|
||
|
||
enum ImpersonationAuditLogAction {
|
||
START
|
||
STOP
|
||
}
|
||
|
||
model ImpersonationAuditLog {
|
||
id String @id @default(cuid())
|
||
|
||
action ImpersonationAuditLogAction
|
||
|
||
/// The admin user who initiated/ended the impersonation
|
||
admin User @relation("ImpersonationAdmin", fields: [adminId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
adminId String
|
||
|
||
/// The user being impersonated
|
||
target User @relation("ImpersonationTarget", fields: [targetId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
targetId String
|
||
|
||
ipAddress String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([adminId])
|
||
@@index([targetId])
|
||
@@index([createdAt])
|
||
}
|
||
|
||
enum CustomerQuerySource {
|
||
DASHBOARD
|
||
API
|
||
}
|
||
|
||
enum CustomerQueryScope {
|
||
ORGANIZATION
|
||
PROJECT
|
||
ENVIRONMENT
|
||
}
|
||
|
||
model CustomerQuery {
|
||
id String @id @default(cuid())
|
||
|
||
/// The TSQL query text that was executed
|
||
query String
|
||
|
||
/// The scope of the query (determines which tenant IDs were used)
|
||
scope CustomerQueryScope
|
||
|
||
/// Query execution statistics from ClickHouse
|
||
stats Json
|
||
|
||
/// AI-generated title summarizing the query
|
||
title String?
|
||
|
||
/// Where the query originated from
|
||
source CustomerQuerySource @default(DASHBOARD)
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String?
|
||
|
||
environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String?
|
||
|
||
/// Optional user who executed the query (null for API calls)
|
||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
userId String?
|
||
|
||
/// Time filter settings used when the query was executed
|
||
/// Period like "7d", "24h", etc.
|
||
filterPeriod String?
|
||
/// Custom start date (ISO 8601)
|
||
filterFrom DateTime?
|
||
/// Custom end date (ISO 8601)
|
||
filterTo DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
/// Fast lookup for history menu (most recent 20 per org)
|
||
@@index([organizationId, createdAt(sort: Desc)])
|
||
/// For Stripe metering job - find unprocessed queries
|
||
@@index([createdAt])
|
||
}
|
||
|
||
model IntegrationDeployment {
|
||
id String @id @default(cuid())
|
||
|
||
integrationName String /// For example Vercel
|
||
integrationDeploymentId String /// External ID
|
||
commitSHA String
|
||
deploymentId String?
|
||
status String? /// External deployment status
|
||
|
||
workerDeployment WorkerDeployment? @relation(fields: [deploymentId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([commitSHA])
|
||
@@index([deploymentId])
|
||
}
|
||
|
||
/// A user-defined metrics dashboard
|
||
model MetricsDashboard {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique
|
||
|
||
title String
|
||
description String @default("")
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String?
|
||
|
||
/// Who created the dashboard
|
||
owner User? @relation(fields: [ownerId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
ownerId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// JSON that defines the config, queries, layout and config of all widgets.
|
||
/// There will be a version field for the format.
|
||
layout String
|
||
|
||
/// Fast lookup for the list
|
||
@@index([projectId, createdAt(sort: Desc)])
|
||
}
|
||
|
||
// ====================================================
|
||
// LLM Pricing Models
|
||
// ====================================================
|
||
|
||
/// A known LLM model or model pattern for cost tracking
|
||
model LlmModel {
|
||
id String @id @default(cuid())
|
||
friendlyId String @unique @map("friendly_id")
|
||
projectId String? @map("project_id")
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||
modelName String @map("model_name")
|
||
matchPattern String @map("match_pattern")
|
||
startDate DateTime? @map("start_date")
|
||
source String @default("default") // "default", "admin", "project"
|
||
createdAt DateTime @default(now()) @map("created_at")
|
||
updatedAt DateTime @updatedAt @map("updated_at")
|
||
|
||
// Catalog metadata for model registry
|
||
provider String? @map("provider")
|
||
description String? @map("description")
|
||
contextWindow Int? @map("context_window")
|
||
maxOutputTokens Int? @map("max_output_tokens")
|
||
capabilities String[] @default([]) @map("capabilities")
|
||
isHidden Boolean @default(false) @map("is_hidden")
|
||
baseModelName String? @map("base_model_name")
|
||
// "tokens", "images", "characters", "minutes", "requests", "free", "not_findable"; null until researched
|
||
pricingUnit String? @map("pricing_unit")
|
||
|
||
pricingTiers LlmPricingTier[]
|
||
prices LlmPrice[]
|
||
|
||
@@unique([projectId, modelName, startDate])
|
||
@@index([projectId])
|
||
@@map("llm_models")
|
||
}
|
||
|
||
/// A pricing tier for a model (supports volume-based or conditional pricing)
|
||
model LlmPricingTier {
|
||
id String @id @default(cuid())
|
||
modelId String @map("model_id")
|
||
model LlmModel @relation(fields: [modelId], references: [id], onDelete: Cascade)
|
||
name String
|
||
isDefault Boolean @default(true) @map("is_default")
|
||
priority Int @default(0)
|
||
conditions Json @default("[]") @db.JsonB
|
||
|
||
prices LlmPrice[]
|
||
|
||
@@unique([modelId, priority])
|
||
@@unique([modelId, name])
|
||
@@map("llm_pricing_tiers")
|
||
}
|
||
|
||
/// A price point for a usage type within a pricing tier
|
||
model LlmPrice {
|
||
id String @id @default(cuid())
|
||
modelId String @map("model_id")
|
||
model LlmModel @relation(fields: [modelId], references: [id], onDelete: Cascade)
|
||
pricingTierId String @map("pricing_tier_id")
|
||
pricingTier LlmPricingTier @relation(fields: [pricingTierId], references: [id], onDelete: Cascade)
|
||
usageType String @map("usage_type")
|
||
price Decimal @db.Decimal(20, 12)
|
||
|
||
@@unique([modelId, usageType, pricingTierId])
|
||
@@map("llm_prices")
|
||
}
|
||
|
||
enum PlatformNotificationSurface {
|
||
WEBAPP
|
||
CLI
|
||
}
|
||
|
||
enum PlatformNotificationScope {
|
||
USER
|
||
PROJECT
|
||
ORGANIZATION
|
||
GLOBAL
|
||
}
|
||
|
||
/// Admin-created notification definitions
|
||
model PlatformNotification {
|
||
id String @id @default(cuid())
|
||
|
||
friendlyId String @unique @default(cuid())
|
||
|
||
/// Admin-facing title for identification in admin tools
|
||
title String
|
||
|
||
/// Versioned JSON rendering content (see payload schema in spec)
|
||
payload Json
|
||
|
||
surface PlatformNotificationSurface
|
||
scope PlatformNotificationScope
|
||
|
||
/// Set when scope = USER
|
||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
userId String?
|
||
|
||
/// Set when scope = ORGANIZATION
|
||
organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String?
|
||
|
||
/// Set when scope = PROJECT
|
||
project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String?
|
||
|
||
/// When notification becomes active
|
||
startsAt DateTime
|
||
|
||
/// When notification expires
|
||
endsAt DateTime
|
||
|
||
/// CLI: auto-expire N days after user first saw it
|
||
cliMaxDaysAfterFirstSeen Int?
|
||
|
||
/// CLI: auto-expire after shown N times to user
|
||
cliMaxShowCount Int?
|
||
|
||
/// CLI: only show every N-th request (e.g. cliShowEvery=3 means show on 3rd, 6th, 9th…)
|
||
cliShowEvery Int?
|
||
|
||
/// Ordering within same scope level (higher = more important)
|
||
priority Int @default(0)
|
||
|
||
/// Staged-but-unpublished. While true the notification is hidden from all
|
||
/// user-facing reads regardless of startsAt/endsAt; publishing sets real
|
||
/// dates and flips this to false.
|
||
isDraft Boolean @default(false)
|
||
|
||
/// Soft delete
|
||
archivedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
interactions PlatformNotificationInteraction[]
|
||
|
||
@@index([surface, scope, startsAt])
|
||
}
|
||
|
||
/// Per-user tracking of notification views and dismissals
|
||
model PlatformNotificationInteraction {
|
||
id String @id @default(cuid())
|
||
|
||
notification PlatformNotification @relation(fields: [notificationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
notificationId String
|
||
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
userId String
|
||
|
||
/// Set by beacon/CLI GET on first impression
|
||
firstSeenAt DateTime @default(now())
|
||
|
||
/// Times shown (incremented per beacon/CLI view)
|
||
showCount Int @default(1)
|
||
|
||
/// User dismissed in webapp
|
||
webappDismissedAt DateTime?
|
||
|
||
/// User clicked a link in webapp
|
||
webappClickedAt DateTime?
|
||
|
||
/// Auto-dismissed or expired in CLI
|
||
cliDismissedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([notificationId, userId])
|
||
}
|
||
|
||
model LogsSearchProjectorCheckpoint {
|
||
id BigInt @id @default(autoincrement())
|
||
|
||
projectorId String
|
||
mode String
|
||
windowStart DateTime
|
||
windowEnd DateTime
|
||
queryId String?
|
||
completedAt DateTime @default(now())
|
||
|
||
@@unique([projectorId, mode, windowStart, windowEnd])
|
||
@@index([projectorId, mode, windowEnd(sort: Desc)])
|
||
}
|
||
|
||
enum ErrorGroupStatus {
|
||
UNRESOLVED
|
||
RESOLVED
|
||
IGNORED
|
||
}
|
||
|
||
/**
|
||
* Error group state is used to track when a user has interacted with an error (ignored/resolved)
|
||
* The actual error data is in ClickHouse.
|
||
*/
|
||
model ErrorGroupState {
|
||
id String @id @default(cuid())
|
||
|
||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
organizationId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
/**
|
||
* You can ignore/resolve an error across all environments, or specific ones
|
||
*/
|
||
environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
environmentId String?
|
||
|
||
taskIdentifier String
|
||
errorFingerprint String
|
||
|
||
status ErrorGroupStatus @default(UNRESOLVED)
|
||
|
||
/**
|
||
* Error is ignored until this date
|
||
*/
|
||
ignoredUntil DateTime?
|
||
/**
|
||
* Error is ignored until this occurrence rate
|
||
*/
|
||
ignoredUntilOccurrenceRate Int?
|
||
/**
|
||
* Error is ignored until this total occurrences
|
||
*/
|
||
ignoredUntilTotalOccurrences Int?
|
||
|
||
/// Total occurrence count at the time the error was ignored (from ClickHouse).
|
||
/// Used with ignoredUntilTotalOccurrences to compute occurrences since ignoring.
|
||
ignoredAtOccurrenceCount BigInt?
|
||
|
||
/**
|
||
* Error was ignored at this date
|
||
*/
|
||
ignoredAt DateTime?
|
||
/**
|
||
* Reason for ignoring the error
|
||
*/
|
||
ignoredReason String?
|
||
/**
|
||
* User who ignored the error
|
||
*/
|
||
ignoredByUserId String?
|
||
|
||
/**
|
||
* Error was resolved at this date
|
||
*/
|
||
resolvedAt DateTime?
|
||
/**
|
||
* Error was resolved in this version
|
||
*/
|
||
resolvedInVersion String?
|
||
/**
|
||
* User who resolved the error
|
||
*/
|
||
resolvedBy String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([environmentId, taskIdentifier, errorFingerprint])
|
||
@@index([environmentId, status])
|
||
}
|
||
|
||
model TaskIdentifier {
|
||
id String @id @default(cuid())
|
||
|
||
runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
runtimeEnvironmentId String
|
||
|
||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||
projectId String
|
||
|
||
slug String
|
||
|
||
currentTriggerSource TaskTriggerSource @default(STANDARD)
|
||
|
||
currentWorker BackgroundWorker? @relation(fields: [currentWorkerId], references: [id], onDelete: SetNull, onUpdate: Cascade)
|
||
currentWorkerId String?
|
||
|
||
firstSeenAt DateTime @default(now())
|
||
lastSeenAt DateTime @default(now())
|
||
isInLatestDeployment Boolean @default(true)
|
||
|
||
@@unique([runtimeEnvironmentId, slug])
|
||
@@index([runtimeEnvironmentId, isInLatestDeployment])
|
||
}
|
||
|
||
enum DataStoreKind {
|
||
CLICKHOUSE
|
||
}
|
||
|
||
/// Defines org-scoped data store overrides (e.g. dedicated ClickHouse for HIPAA orgs).
|
||
/// Multiple organizations can share a single data store row via organizationIds.
|
||
model OrganizationDataStore {
|
||
id String @id @default(cuid())
|
||
/// Human-readable unique key (e.g. "hipaa-clickhouse-us-east")
|
||
key String @unique
|
||
/// Organization IDs that use this data store
|
||
organizationIds String[]
|
||
kind DataStoreKind
|
||
/// Versioned config JSON. Structure is discriminated by the top-level `version` field.
|
||
config Json
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([kind])
|
||
}
|