mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-13 08:14:58 -05:00
- Added `LibraryFolder` model to represent folders in the library. - Updated `LibraryAgent` model to include a reference to `folderId`. - Introduced new API endpoints for folder operations: create, update, move, delete, and list folders. - Enhanced existing agent endpoints to support filtering by folder. - Implemented validation for folder operations to prevent circular references and depth violations. - Created a migration script to add folder-related database schema changes. This feature allows users to organize their library agents into folders, improving the overall user experience and management of agents.
1283 lines
41 KiB
Plaintext
1283 lines
41 KiB
Plaintext
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
directUrl = env("DIRECT_URL")
|
|
extensions = [pgvector(map: "vector")]
|
|
}
|
|
|
|
generator client {
|
|
provider = "prisma-client-py"
|
|
recursive_type_depth = -1
|
|
interface = "asyncio"
|
|
previewFeatures = ["views", "fullTextSearch", "postgresqlExtensions"]
|
|
partial_type_generator = "backend/data/partial_types.py"
|
|
}
|
|
|
|
// User model to mirror Auth provider users
|
|
model User {
|
|
id String @id // This should match the Supabase user ID
|
|
email String @unique
|
|
emailVerified Boolean @default(true)
|
|
name String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
metadata Json @default("{}")
|
|
integrations String @default("")
|
|
stripeCustomerId String?
|
|
topUpConfig Json?
|
|
|
|
maxEmailsPerDay Int @default(3)
|
|
notifyOnAgentRun Boolean @default(true)
|
|
notifyOnZeroBalance Boolean @default(true)
|
|
notifyOnLowBalance Boolean @default(true)
|
|
notifyOnBlockExecutionFailed Boolean @default(true)
|
|
notifyOnContinuousAgentError Boolean @default(true)
|
|
notifyOnDailySummary Boolean @default(true)
|
|
notifyOnWeeklySummary Boolean @default(true)
|
|
notifyOnMonthlySummary Boolean @default(true)
|
|
notifyOnAgentApproved Boolean @default(true)
|
|
notifyOnAgentRejected Boolean @default(true)
|
|
|
|
timezone String @default("not-set")
|
|
|
|
// Relations
|
|
|
|
AgentGraphs AgentGraph[]
|
|
AgentGraphExecutions AgentGraphExecution[]
|
|
AnalyticsDetails AnalyticsDetails[]
|
|
AnalyticsMetrics AnalyticsMetrics[]
|
|
CreditTransactions CreditTransaction[]
|
|
UserBalance UserBalance?
|
|
ChatSessions ChatSession[]
|
|
AgentPresets AgentPreset[]
|
|
LibraryAgents LibraryAgent[]
|
|
LibraryFolders LibraryFolder[]
|
|
|
|
Profile Profile[]
|
|
UserOnboarding UserOnboarding?
|
|
CoPilotUnderstanding CoPilotUnderstanding?
|
|
BuilderSearchHistory BuilderSearchHistory[]
|
|
StoreListings StoreListing[]
|
|
StoreListingReviews StoreListingReview[]
|
|
StoreVersionsReviewed StoreListingVersion[]
|
|
APIKeys APIKey[]
|
|
IntegrationWebhooks IntegrationWebhook[]
|
|
NotificationBatches UserNotificationBatch[]
|
|
PendingHumanReviews PendingHumanReview[]
|
|
Workspace UserWorkspace?
|
|
|
|
// OAuth Provider relations
|
|
OAuthApplications OAuthApplication[]
|
|
OAuthAuthorizationCodes OAuthAuthorizationCode[]
|
|
OAuthAccessTokens OAuthAccessToken[]
|
|
OAuthRefreshTokens OAuthRefreshToken[]
|
|
}
|
|
|
|
enum OnboardingStep {
|
|
// Introductory onboarding (Library)
|
|
WELCOME
|
|
USAGE_REASON
|
|
INTEGRATIONS
|
|
AGENT_CHOICE
|
|
AGENT_NEW_RUN
|
|
AGENT_INPUT
|
|
CONGRATS
|
|
// First Wins
|
|
VISIT_COPILOT
|
|
GET_RESULTS
|
|
MARKETPLACE_VISIT
|
|
MARKETPLACE_ADD_AGENT
|
|
MARKETPLACE_RUN_AGENT
|
|
BUILDER_SAVE_AGENT
|
|
// Consistency Challenge
|
|
RE_RUN_AGENT
|
|
SCHEDULE_AGENT
|
|
RUN_AGENTS
|
|
RUN_3_DAYS
|
|
// The Pro Playground
|
|
TRIGGER_WEBHOOK
|
|
RUN_14_DAYS
|
|
RUN_AGENTS_100
|
|
// No longer rewarded but exist for analytical purposes
|
|
BUILDER_OPEN
|
|
BUILDER_RUN_AGENT
|
|
}
|
|
|
|
model UserOnboarding {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
completedSteps OnboardingStep[] @default([])
|
|
walletShown Boolean @default(false)
|
|
notified OnboardingStep[] @default([])
|
|
rewardedFor OnboardingStep[] @default([])
|
|
usageReason String?
|
|
integrations String[] @default([])
|
|
otherIntegrations String?
|
|
selectedStoreListingVersionId String?
|
|
agentInput Json?
|
|
onboardingAgentExecutionId String?
|
|
agentRuns Int @default(0)
|
|
lastRunAt DateTime?
|
|
consecutiveRunDays Int @default(0)
|
|
|
|
userId String @unique
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model CoPilotUnderstanding {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String @unique
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
data Json?
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////////////// USER WORKSPACE TABLES /////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// User's persistent file storage workspace
|
|
model UserWorkspace {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
userId String @unique
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
Files UserWorkspaceFile[]
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
// Individual files in a user's workspace
|
|
model UserWorkspaceFile {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
workspaceId String
|
|
Workspace UserWorkspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
|
|
// File metadata
|
|
name String // User-visible filename
|
|
path String // Virtual path (e.g., "/documents/report.pdf")
|
|
storagePath String // Actual GCS or local storage path
|
|
mimeType String
|
|
sizeBytes BigInt
|
|
checksum String? // SHA256 for integrity
|
|
|
|
// File state
|
|
isDeleted Boolean @default(false)
|
|
deletedAt DateTime?
|
|
|
|
metadata Json @default("{}")
|
|
|
|
@@unique([workspaceId, path])
|
|
@@index([workspaceId, isDeleted])
|
|
}
|
|
|
|
model BuilderSearchHistory {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
searchQuery String
|
|
filter String[] @default([])
|
|
byCreator String[] @default([])
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////////////// CHAT SESSION TABLES ///////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
model ChatSession {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
// Session metadata
|
|
title String?
|
|
credentials Json @default("{}") // Map of provider -> credential metadata
|
|
|
|
// Rate limiting counters (stored as JSON maps)
|
|
successfulAgentRuns Json @default("{}") // Map of graph_id -> count
|
|
successfulAgentSchedules Json @default("{}") // Map of graph_id -> count
|
|
|
|
// Usage tracking
|
|
totalPromptTokens Int @default(0)
|
|
totalCompletionTokens Int @default(0)
|
|
|
|
Messages ChatMessage[]
|
|
|
|
@@index([userId, updatedAt])
|
|
}
|
|
|
|
model ChatMessage {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
sessionId String
|
|
Session ChatSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
|
|
// Message content
|
|
role String // "user", "assistant", "system", "tool", "function"
|
|
content String?
|
|
name String?
|
|
toolCallId String?
|
|
refusal String?
|
|
toolCalls Json? // List of tool calls for assistant messages
|
|
functionCall Json? // Deprecated but kept for compatibility
|
|
|
|
// Ordering within session
|
|
sequence Int
|
|
|
|
@@unique([sessionId, sequence])
|
|
}
|
|
|
|
// This model describes the Agent Graph/Flow (Multi Agent System).
|
|
model AgentGraph {
|
|
id String @default(uuid())
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
name String?
|
|
description String?
|
|
instructions String?
|
|
recommendedScheduleCron String?
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
// Link to User model
|
|
userId String
|
|
// FIX: Do not cascade delete the agent when the user is deleted
|
|
// This allows us to delete user data with deleting the agent which maybe in use by other users
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
forkedFromId String?
|
|
forkedFromVersion Int?
|
|
forkedFrom AgentGraph? @relation("AgentGraphForks", fields: [forkedFromId, forkedFromVersion], references: [id, version])
|
|
forks AgentGraph[] @relation("AgentGraphForks")
|
|
|
|
Nodes AgentNode[]
|
|
Executions AgentGraphExecution[]
|
|
|
|
Presets AgentPreset[]
|
|
LibraryAgents LibraryAgent[]
|
|
StoreListings StoreListing[]
|
|
StoreListingVersions StoreListingVersion[]
|
|
|
|
@@id(name: "graphVersionId", [id, version])
|
|
@@index([userId, isActive, id, version])
|
|
@@index([forkedFromId, forkedFromVersion])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////////////// USER SPECIFIC DATA ////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// An AgentPrest is an Agent + User Configuration of that agent.
|
|
// For example, if someone has created a weather agent and they want to set it up to
|
|
// Inform them of extreme weather warnings in Texas, the agent with the configuration to set it to
|
|
// monitor texas, along with the cron setup or webhook tiggers, is an AgentPreset
|
|
model AgentPreset {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
name String
|
|
description String
|
|
|
|
// For agents that can be triggered by webhooks or cronjob
|
|
// This bool allows us to disable a configured agent without deleting it
|
|
isActive Boolean @default(true)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Restrict)
|
|
|
|
InputPresets AgentNodeExecutionInputOutput[] @relation("AgentPresetsInputData")
|
|
Executions AgentGraphExecution[]
|
|
|
|
// For webhook-triggered agents: reference to the webhook that triggers the agent
|
|
webhookId String?
|
|
Webhook IntegrationWebhook? @relation(fields: [webhookId], references: [id])
|
|
|
|
isDeleted Boolean @default(false)
|
|
|
|
@@index([userId])
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([webhookId])
|
|
}
|
|
|
|
enum NotificationType {
|
|
AGENT_RUN
|
|
ZERO_BALANCE
|
|
LOW_BALANCE
|
|
BLOCK_EXECUTION_FAILED
|
|
CONTINUOUS_AGENT_ERROR
|
|
DAILY_SUMMARY
|
|
WEEKLY_SUMMARY
|
|
MONTHLY_SUMMARY
|
|
REFUND_REQUEST
|
|
REFUND_PROCESSED
|
|
AGENT_APPROVED
|
|
AGENT_REJECTED
|
|
}
|
|
|
|
model NotificationEvent {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
UserNotificationBatch UserNotificationBatch? @relation(fields: [userNotificationBatchId], references: [id])
|
|
userNotificationBatchId String?
|
|
|
|
type NotificationType
|
|
data Json
|
|
|
|
@@index([userNotificationBatchId])
|
|
}
|
|
|
|
model UserNotificationBatch {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
type NotificationType
|
|
|
|
Notifications NotificationEvent[]
|
|
|
|
// Each user can only have one batch of a notification type at a time
|
|
@@unique([userId, type])
|
|
}
|
|
|
|
// For the library page
|
|
// It is a user controlled list of agents, that they will see in their library
|
|
model LibraryAgent {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
imageUrl String?
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Restrict)
|
|
|
|
creatorId String?
|
|
Creator Profile? @relation(fields: [creatorId], references: [id])
|
|
|
|
folderId String?
|
|
Folder LibraryFolder? @relation(fields: [folderId], references: [id], onDelete: Restrict)
|
|
|
|
useGraphIsActiveVersion Boolean @default(false)
|
|
|
|
isFavorite Boolean @default(false)
|
|
isCreatedByUser Boolean @default(false)
|
|
isArchived Boolean @default(false)
|
|
isDeleted Boolean @default(false)
|
|
|
|
settings Json @default("{}")
|
|
|
|
@@unique([userId, agentGraphId, agentGraphVersion])
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([creatorId])
|
|
@@index([folderId])
|
|
}
|
|
|
|
model LibraryFolder {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
name String
|
|
icon String?
|
|
color String?
|
|
|
|
parentId String?
|
|
Parent LibraryFolder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
|
|
Children LibraryFolder[] @relation("FolderHierarchy")
|
|
|
|
isDeleted Boolean @default(false)
|
|
|
|
LibraryAgents LibraryAgent[]
|
|
|
|
@@unique([userId, parentId, name]) // Name unique per parent per user
|
|
@@index([userId, isDeleted])
|
|
@@index([parentId])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////// AGENT DEFINITION AND EXECUTION TABLES ////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// This model describes a single node in the Agent Graph/Flow (Multi Agent System).
|
|
model AgentNode {
|
|
id String @id @default(uuid())
|
|
|
|
agentBlockId String
|
|
AgentBlock AgentBlock @relation(fields: [agentBlockId], references: [id], onUpdate: Cascade)
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int @default(1)
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Cascade)
|
|
|
|
// List of consumed input, that the parent node should provide.
|
|
Input AgentNodeLink[] @relation("AgentNodeSink")
|
|
|
|
// List of produced output, that the child node should be executed.
|
|
Output AgentNodeLink[] @relation("AgentNodeSource")
|
|
|
|
constantInput Json @default("{}")
|
|
|
|
// For webhook-triggered blocks: reference to the webhook that triggers the node
|
|
webhookId String?
|
|
Webhook IntegrationWebhook? @relation(fields: [webhookId], references: [id])
|
|
|
|
metadata Json @default("{}")
|
|
|
|
Executions AgentNodeExecution[]
|
|
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([agentBlockId])
|
|
@@index([webhookId])
|
|
}
|
|
|
|
// This model describes the link between two AgentNodes.
|
|
model AgentNodeLink {
|
|
id String @id @default(uuid())
|
|
|
|
// Output of a node is connected to the source of the link.
|
|
agentNodeSourceId String
|
|
AgentNodeSource AgentNode @relation("AgentNodeSource", fields: [agentNodeSourceId], references: [id], onDelete: Cascade)
|
|
sourceName String
|
|
|
|
// Input of a node is connected to the sink of the link.
|
|
agentNodeSinkId String
|
|
AgentNodeSink AgentNode @relation("AgentNodeSink", fields: [agentNodeSinkId], references: [id], onDelete: Cascade)
|
|
sinkName String
|
|
|
|
// Default: the data coming from the source can only be consumed by the sink once, Static: input data will be reused.
|
|
isStatic Boolean @default(false)
|
|
|
|
@@index([agentNodeSourceId])
|
|
@@index([agentNodeSinkId])
|
|
}
|
|
|
|
// This model describes a component that will be executed by the AgentNode.
|
|
model AgentBlock {
|
|
id String @id @default(uuid())
|
|
name String @unique
|
|
|
|
// We allow a block to have multiple types of input & output.
|
|
// Serialized object-typed `jsonschema` with top-level properties as input/output name.
|
|
inputSchema String
|
|
outputSchema String
|
|
|
|
// Prisma requires explicit back-references.
|
|
ReferencedByAgentNode AgentNode[]
|
|
}
|
|
|
|
// This model describes the status of an AgentGraphExecution or AgentNodeExecution.
|
|
enum AgentExecutionStatus {
|
|
INCOMPLETE
|
|
QUEUED
|
|
RUNNING
|
|
COMPLETED
|
|
TERMINATED
|
|
FAILED
|
|
REVIEW
|
|
}
|
|
|
|
// This model describes the execution of an AgentGraph.
|
|
model AgentGraphExecution {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
startedAt DateTime?
|
|
endedAt DateTime?
|
|
|
|
isDeleted Boolean @default(false)
|
|
|
|
executionStatus AgentExecutionStatus @default(COMPLETED)
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int @default(1)
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Cascade)
|
|
|
|
agentPresetId String?
|
|
AgentPreset AgentPreset? @relation(fields: [agentPresetId], references: [id])
|
|
|
|
inputs Json?
|
|
credentialInputs Json?
|
|
nodesInputMasks Json?
|
|
|
|
NodeExecutions AgentNodeExecution[]
|
|
|
|
// Link to User model -- Executed by this user
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
stats Json?
|
|
|
|
// Parent-child execution tracking for nested agent graphs
|
|
parentGraphExecutionId String?
|
|
ParentExecution AgentGraphExecution? @relation("ParentChildExecution", fields: [parentGraphExecutionId], references: [id], onDelete: SetNull)
|
|
ChildExecutions AgentGraphExecution[] @relation("ParentChildExecution")
|
|
|
|
// Sharing fields
|
|
isShared Boolean @default(false)
|
|
shareToken String? @unique
|
|
sharedAt DateTime?
|
|
|
|
PendingHumanReviews PendingHumanReview[]
|
|
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([userId, isDeleted, createdAt])
|
|
@@index([createdAt])
|
|
@@index([agentPresetId])
|
|
@@index([shareToken])
|
|
@@index([parentGraphExecutionId])
|
|
}
|
|
|
|
// This model describes the execution of an AgentNode.
|
|
model AgentNodeExecution {
|
|
id String @id @default(uuid())
|
|
|
|
agentGraphExecutionId String
|
|
GraphExecution AgentGraphExecution @relation(fields: [agentGraphExecutionId], references: [id], onDelete: Cascade)
|
|
|
|
agentNodeId String
|
|
Node AgentNode @relation(fields: [agentNodeId], references: [id], onDelete: Cascade)
|
|
|
|
Input AgentNodeExecutionInputOutput[] @relation("AgentNodeExecutionInput")
|
|
Output AgentNodeExecutionInputOutput[] @relation("AgentNodeExecutionOutput")
|
|
|
|
executionStatus AgentExecutionStatus @default(COMPLETED)
|
|
executionData Json?
|
|
addedTime DateTime @default(now())
|
|
queuedTime DateTime?
|
|
startedTime DateTime?
|
|
endedTime DateTime?
|
|
|
|
stats Json?
|
|
|
|
@@index([agentGraphExecutionId, agentNodeId, executionStatus])
|
|
@@index([agentNodeId, executionStatus])
|
|
@@index([addedTime, queuedTime])
|
|
}
|
|
|
|
// This model describes the output of an AgentNodeExecution.
|
|
model AgentNodeExecutionInputOutput {
|
|
id String @id @default(uuid())
|
|
|
|
name String
|
|
data Json?
|
|
time DateTime @default(now())
|
|
|
|
// Prisma requires explicit back-references.
|
|
referencedByInputExecId String?
|
|
ReferencedByInputExec AgentNodeExecution? @relation("AgentNodeExecutionInput", fields: [referencedByInputExecId], references: [id], onDelete: Cascade)
|
|
referencedByOutputExecId String?
|
|
ReferencedByOutputExec AgentNodeExecution? @relation("AgentNodeExecutionOutput", fields: [referencedByOutputExecId], references: [id], onDelete: Cascade)
|
|
|
|
agentPresetId String?
|
|
AgentPreset AgentPreset? @relation("AgentPresetsInputData", fields: [agentPresetId], references: [id])
|
|
|
|
// Input and Output pin names are unique for each AgentNodeExecution.
|
|
@@unique([referencedByInputExecId, referencedByOutputExecId, name])
|
|
@@index([referencedByOutputExecId])
|
|
// Composite index for `upsert_execution_input`.
|
|
@@index([name, time])
|
|
@@index([agentPresetId])
|
|
}
|
|
|
|
model AgentNodeExecutionKeyValueData {
|
|
userId String
|
|
key String
|
|
agentNodeExecutionId String
|
|
data Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
@@id([userId, key])
|
|
}
|
|
|
|
enum ReviewStatus {
|
|
WAITING
|
|
APPROVED
|
|
REJECTED
|
|
}
|
|
|
|
// Pending human reviews for Human-in-the-loop blocks
|
|
// Also stores auto-approval records with special nodeExecId patterns (e.g., "auto_approve_{graph_exec_id}_{node_id}")
|
|
model PendingHumanReview {
|
|
nodeExecId String @id
|
|
userId String
|
|
graphExecId String
|
|
graphId String
|
|
graphVersion Int
|
|
payload Json // The actual payload data to be reviewed
|
|
instructions String? // Instructions/message for the reviewer
|
|
editable Boolean @default(true) // Whether the reviewer can edit the data
|
|
status ReviewStatus @default(WAITING)
|
|
reviewMessage String? // Optional message from the reviewer
|
|
wasEdited Boolean? // Whether the data was modified during review
|
|
processed Boolean @default(false) // Whether the review result has been processed by the execution engine
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
reviewedAt DateTime?
|
|
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
GraphExecution AgentGraphExecution @relation(fields: [graphExecId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([nodeExecId]) // One pending review per node execution
|
|
@@index([userId, status])
|
|
@@index([graphExecId, status])
|
|
}
|
|
|
|
// Webhook that is registered with a provider and propagates to one or more nodes
|
|
model IntegrationWebhook {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Restrict) // Webhooks must be deregistered before deleting
|
|
|
|
provider String // e.g. 'github'
|
|
credentialsId String // relation to the credentials that the webhook was created with
|
|
webhookType String // e.g. 'repo'
|
|
resource String // e.g. 'Significant-Gravitas/AutoGPT'
|
|
events String[] // e.g. ['created', 'updated']
|
|
config Json
|
|
secret String // crypto string, used to verify payload authenticity
|
|
|
|
providerWebhookId String // Webhook ID assigned by the provider
|
|
|
|
AgentNodes AgentNode[]
|
|
AgentPresets AgentPreset[]
|
|
}
|
|
|
|
model AnalyticsDetails {
|
|
// PK uses gen_random_uuid() to allow the db inserts to happen outside of prisma
|
|
// typical uuid() inserts are handled by prisma
|
|
id String @id @default(dbgenerated("gen_random_uuid()"))
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
// Link to User model
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
// Analytics Categorical data used for filtering (indexable w and w/o userId)
|
|
type String
|
|
|
|
// Analytic Specific Data. We should use a union type here, but prisma doesn't support it.
|
|
data Json?
|
|
|
|
// Indexable field for any count based analytical measures like page order clicking, tutorial step completion, etc.
|
|
dataIndex String?
|
|
|
|
@@index([userId, type])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////// METRICS TRACKING TABLES ////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
model AnalyticsMetrics {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Analytics Categorical data used for filtering (indexable w and w/o userId)
|
|
analyticMetric String
|
|
// Any numeric data that should be counted upon, summed, or otherwise aggregated.
|
|
value Float
|
|
// Any string data that should be used to identify the metric as distinct.
|
|
// ex: '/build' vs '/market'
|
|
dataString String?
|
|
|
|
// Link to User model
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////// ACCOUNTING AND CREDIT SYSTEM TABLES //////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
enum CreditTransactionType {
|
|
TOP_UP
|
|
USAGE
|
|
GRANT
|
|
REFUND
|
|
CARD_CHECK
|
|
}
|
|
|
|
model CreditTransaction {
|
|
transactionKey String @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
userId String
|
|
User User? @relation(fields: [userId], references: [id], onDelete: NoAction)
|
|
|
|
amount Int
|
|
type CreditTransactionType
|
|
|
|
runningBalance Int?
|
|
|
|
isActive Boolean @default(true)
|
|
metadata Json?
|
|
|
|
@@id(name: "creditTransactionIdentifier", [transactionKey, userId])
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
enum CreditRefundRequestStatus {
|
|
PENDING
|
|
APPROVED
|
|
REJECTED
|
|
}
|
|
|
|
model CreditRefundRequest {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
userId String
|
|
transactionKey String
|
|
|
|
amount Int
|
|
reason String
|
|
result String?
|
|
status CreditRefundRequestStatus @default(PENDING)
|
|
|
|
@@index([userId, transactionKey])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////// Store TABLES ///////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
model SearchTerms {
|
|
// User ID not being logged as this is anonymous analytics data
|
|
// Not using uuid as we want to minimise table size
|
|
id BigInt @id @default(autoincrement())
|
|
createdDate DateTime
|
|
searchTerm String
|
|
|
|
@@index([createdDate])
|
|
}
|
|
|
|
model Profile {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
// Only 1 of user or group can be set.
|
|
// The user this profile belongs to, if any.
|
|
userId String?
|
|
User User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
name String
|
|
username String @unique
|
|
description String
|
|
|
|
links String[]
|
|
|
|
avatarUrl String?
|
|
|
|
isFeatured Boolean @default(false)
|
|
|
|
LibraryAgents LibraryAgent[]
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
view Creator {
|
|
username String @unique
|
|
name String
|
|
avatar_url String
|
|
description String
|
|
|
|
top_categories String[]
|
|
links String[]
|
|
|
|
num_agents Int
|
|
agent_rating Float
|
|
agent_runs Int
|
|
is_featured Boolean
|
|
|
|
// Materialized views used (refreshed every 15 minutes via pg_cron):
|
|
// - mv_agent_run_counts - Pre-aggregated agent execution counts by agentGraphId
|
|
// * idx_mv_agent_run_counts (UNIQUE on agentGraphId) - Primary lookup
|
|
// - mv_review_stats - Pre-aggregated review statistics (count, avg rating) by storeListingId
|
|
// * idx_mv_review_stats (UNIQUE on storeListingId) - Primary lookup
|
|
// * idx_mv_review_stats_rating (avg_rating DESC) - Sort by rating performance
|
|
// * idx_mv_review_stats_count (review_count DESC) - Sort by review count performance
|
|
//
|
|
// Query strategy: Uses CTEs to efficiently aggregate creator statistics leveraging materialized views
|
|
}
|
|
|
|
view StoreAgent {
|
|
listing_id String @id
|
|
storeListingVersionId String
|
|
updated_at DateTime
|
|
|
|
slug String
|
|
agent_name String
|
|
agent_video String?
|
|
agent_output_demo String?
|
|
agent_image String[]
|
|
|
|
featured Boolean @default(false)
|
|
creator_username String?
|
|
creator_avatar String?
|
|
sub_heading String
|
|
description String
|
|
categories String[]
|
|
runs Int
|
|
rating Float
|
|
versions String[]
|
|
agentGraphVersions String[]
|
|
agentGraphId String
|
|
is_available Boolean @default(true)
|
|
useForOnboarding Boolean @default(false)
|
|
|
|
// Materialized views used (refreshed every 15 minutes via pg_cron):
|
|
// - mv_agent_run_counts - Pre-aggregated agent execution counts by agentGraphId
|
|
// * idx_mv_agent_run_counts (UNIQUE on agentGraphId) - Primary lookup
|
|
// - mv_review_stats - Pre-aggregated review statistics (count, avg rating) by storeListingId
|
|
// * idx_mv_review_stats (UNIQUE on storeListingId) - Primary lookup
|
|
// * idx_mv_review_stats_rating (avg_rating DESC) - Sort by rating performance
|
|
// * idx_mv_review_stats_count (review_count DESC) - Sort by review count performance
|
|
//
|
|
// Query strategy: Uses CTE for version aggregation and joins with materialized views for performance
|
|
}
|
|
|
|
view StoreSubmission {
|
|
listing_id String @id
|
|
user_id String
|
|
slug String
|
|
name String
|
|
sub_heading String
|
|
description String
|
|
image_urls String[]
|
|
date_submitted DateTime
|
|
status SubmissionStatus
|
|
runs Int
|
|
rating Float
|
|
agent_id String
|
|
agent_version Int
|
|
store_listing_version_id String
|
|
reviewer_id String?
|
|
review_comments String?
|
|
internal_comments String?
|
|
reviewed_at DateTime?
|
|
changes_summary String?
|
|
video_url String?
|
|
categories String[]
|
|
|
|
// Index or unique are not applied to views
|
|
}
|
|
|
|
// Note: This is actually a MATERIALIZED VIEW in the database
|
|
// Refreshed automatically every 15 minutes via pg_cron (with fallback to manual refresh)
|
|
view mv_agent_run_counts {
|
|
agentGraphId String @unique
|
|
run_count Int
|
|
|
|
// Pre-aggregated count of AgentGraphExecution records by agentGraphId
|
|
// Used by StoreAgent and Creator views for performance optimization
|
|
// Unique index created automatically on agentGraphId for fast lookups
|
|
// Refresh uses CONCURRENTLY to avoid blocking reads
|
|
}
|
|
|
|
// Note: This is actually a MATERIALIZED VIEW in the database
|
|
// Refreshed automatically every 15 minutes via pg_cron (with fallback to manual refresh)
|
|
view mv_review_stats {
|
|
storeListingId String @unique
|
|
review_count Int
|
|
avg_rating Float
|
|
|
|
// Pre-aggregated review statistics from StoreListingReview
|
|
// Includes count of reviews and average rating per StoreListing
|
|
// Only includes approved versions (submissionStatus = 'APPROVED') and non-deleted listings
|
|
// Used by StoreAgent view for performance optimization
|
|
// Unique index created automatically on storeListingId for fast lookups
|
|
// Refresh uses CONCURRENTLY to avoid blocking reads
|
|
}
|
|
|
|
model StoreListing {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
isDeleted Boolean @default(false)
|
|
// Whether any version has been approved and is available for display
|
|
hasApprovedVersion Boolean @default(false)
|
|
|
|
// URL-friendly identifier for this agent (moved from StoreListingVersion)
|
|
slug String
|
|
|
|
// Allow this agent to be used during onboarding
|
|
useForOnboarding Boolean @default(false)
|
|
|
|
// The currently active version that should be shown to users
|
|
activeVersionId String? @unique
|
|
ActiveVersion StoreListingVersion? @relation("ActiveVersion", fields: [activeVersionId], references: [id])
|
|
|
|
// The agent link here is only so we can do lookup on agentId
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Cascade)
|
|
|
|
owningUserId String
|
|
OwningUser User @relation(fields: [owningUserId], references: [id])
|
|
|
|
// Relations
|
|
Versions StoreListingVersion[] @relation("ListingVersions")
|
|
|
|
// Unique index on agentId to ensure only one listing per agent, regardless of number of versions the agent has.
|
|
@@unique([agentGraphId])
|
|
@@unique([owningUserId, slug])
|
|
// Used in the view query
|
|
@@index([isDeleted, hasApprovedVersion])
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
}
|
|
|
|
model StoreListingVersion {
|
|
id String @id @default(uuid())
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
// The agent and version to be listed on the store
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version])
|
|
|
|
// Content fields
|
|
name String
|
|
subHeading String
|
|
videoUrl String?
|
|
agentOutputDemoUrl String?
|
|
imageUrls String[]
|
|
description String
|
|
instructions String?
|
|
categories String[]
|
|
|
|
isFeatured Boolean @default(false)
|
|
|
|
isDeleted Boolean @default(false)
|
|
// Old versions can be made unavailable by the author if desired
|
|
isAvailable Boolean @default(true)
|
|
|
|
// Note: search column removed - now using UnifiedContentEmbedding.search
|
|
|
|
// Version workflow state
|
|
submissionStatus SubmissionStatus @default(DRAFT)
|
|
submittedAt DateTime?
|
|
|
|
// Relations
|
|
storeListingId String
|
|
StoreListing StoreListing @relation("ListingVersions", fields: [storeListingId], references: [id], onDelete: Cascade)
|
|
|
|
// This version might be the active version for a listing
|
|
ActiveFor StoreListing? @relation("ActiveVersion")
|
|
|
|
// Submission history
|
|
changesSummary String?
|
|
|
|
// Review information
|
|
reviewerId String?
|
|
Reviewer User? @relation(fields: [reviewerId], references: [id])
|
|
internalComments String? // Private notes for admin use only
|
|
reviewComments String? // Comments visible to creator
|
|
reviewedAt DateTime?
|
|
|
|
recommendedScheduleCron String? // cron expression like "0 9 * * *"
|
|
|
|
// Reviews for this specific version
|
|
Reviews StoreListingReview[]
|
|
|
|
// Note: Embeddings now stored in UnifiedContentEmbedding table
|
|
// Use contentType=STORE_AGENT and contentId=storeListingVersionId
|
|
|
|
@@unique([storeListingId, version])
|
|
@@index([storeListingId, submissionStatus, isAvailable])
|
|
@@index([submissionStatus])
|
|
@@index([reviewerId])
|
|
@@index([agentGraphId, agentGraphVersion]) // Non-unique index for efficient lookups
|
|
}
|
|
|
|
// Content type enum for unified search across store agents, blocks, docs
|
|
// Note: BLOCK/INTEGRATION are file-based (Python classes), not DB records
|
|
// DOCUMENTATION are file-based (.md files), not DB records
|
|
// Only STORE_AGENT and LIBRARY_AGENT are stored in database
|
|
enum ContentType {
|
|
STORE_AGENT // Database: StoreListingVersion
|
|
BLOCK // File-based: Python classes in /backend/blocks/
|
|
INTEGRATION // File-based: Python classes (blocks with credentials)
|
|
DOCUMENTATION // File-based: .md/.mdx files
|
|
LIBRARY_AGENT // Database: User's personal agents
|
|
}
|
|
|
|
// Unified embeddings table for all searchable content types
|
|
// Supports both public content (userId=null) and user-specific content (userId=userID)
|
|
model UnifiedContentEmbedding {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Content identification
|
|
contentType ContentType
|
|
contentId String // DB ID (storeListingVersionId) or file identifier (block.id, file_path)
|
|
userId String? // NULL for public content (store, blocks, docs), userId for private content (library agents)
|
|
|
|
// Search data
|
|
embedding Unsupported("vector(1536)") // pgvector embedding (extension in platform schema)
|
|
searchableText String // Combined text for search and fallback
|
|
search Unsupported("tsvector")? @default(dbgenerated("''::tsvector")) // Full-text search (auto-populated by trigger)
|
|
metadata Json @default("{}") // Content-specific metadata
|
|
|
|
@@unique([contentType, contentId, userId], map: "UnifiedContentEmbedding_contentType_contentId_userId_key")
|
|
@@index([contentType])
|
|
@@index([userId])
|
|
@@index([contentType, userId])
|
|
@@index([embedding], map: "UnifiedContentEmbedding_embedding_idx")
|
|
// NO @@index for search - GIN index "UnifiedContentEmbedding_search_idx" created via SQL migration
|
|
// Prisma may generate DROP INDEX on migrate dev - that's okay, migration recreates it
|
|
}
|
|
|
|
model StoreListingReview {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
storeListingVersionId String
|
|
StoreListingVersion StoreListingVersion @relation(fields: [storeListingVersionId], references: [id], onDelete: Cascade)
|
|
|
|
reviewByUserId String
|
|
ReviewByUser User @relation(fields: [reviewByUserId], references: [id])
|
|
|
|
score Int
|
|
comments String?
|
|
|
|
@@unique([storeListingVersionId, reviewByUserId])
|
|
@@index([reviewByUserId])
|
|
}
|
|
|
|
enum SubmissionStatus {
|
|
DRAFT // Being prepared, not yet submitted
|
|
PENDING // Submitted, awaiting review
|
|
APPROVED // Reviewed and approved
|
|
REJECTED // Reviewed and rejected
|
|
}
|
|
|
|
enum APIKeyPermission {
|
|
IDENTITY // Info about the authenticated user
|
|
EXECUTE_GRAPH // Can execute agent graphs
|
|
READ_GRAPH // Can get graph versions and details
|
|
EXECUTE_BLOCK // Can execute individual blocks
|
|
READ_BLOCK // Can get block information
|
|
READ_STORE // Can read store agents and creators
|
|
USE_TOOLS // Can use chat tools via external API
|
|
MANAGE_INTEGRATIONS // Can initiate OAuth flows and complete them
|
|
READ_INTEGRATIONS // Can list credentials and providers
|
|
DELETE_INTEGRATIONS // Can delete credentials
|
|
}
|
|
|
|
model APIKey {
|
|
id String @id @default(uuid())
|
|
name String
|
|
head String // First few chars for identification
|
|
tail String
|
|
hash String @unique
|
|
salt String? // null for legacy unsalted keys
|
|
|
|
status APIKeyStatus @default(ACTIVE)
|
|
permissions APIKeyPermission[]
|
|
|
|
createdAt DateTime @default(now())
|
|
lastUsedAt DateTime?
|
|
revokedAt DateTime?
|
|
|
|
description String?
|
|
|
|
// Relation to user
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([head, name])
|
|
@@index([userId, status])
|
|
}
|
|
|
|
model UserBalance {
|
|
userId String @id
|
|
balance Int @default(0)
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
enum APIKeyStatus {
|
|
ACTIVE
|
|
REVOKED
|
|
SUSPENDED
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////// OAUTH PROVIDER TABLES //////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// OAuth2 applications that can access AutoGPT on behalf of users
|
|
model OAuthApplication {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Application metadata
|
|
name String
|
|
description String?
|
|
logoUrl String? // URL to app logo stored in GCS
|
|
clientId String @unique
|
|
clientSecret String // Hashed with Scrypt (same as API keys)
|
|
clientSecretSalt String // Salt for Scrypt hashing
|
|
|
|
// OAuth configuration
|
|
redirectUris String[] // Allowed callback URLs
|
|
grantTypes String[] @default(["authorization_code", "refresh_token"])
|
|
scopes APIKeyPermission[] // Which permissions the app can request
|
|
|
|
// Application management
|
|
ownerId String
|
|
Owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
|
isActive Boolean @default(true)
|
|
|
|
// Relations
|
|
AuthorizationCodes OAuthAuthorizationCode[]
|
|
AccessTokens OAuthAccessToken[]
|
|
RefreshTokens OAuthRefreshToken[]
|
|
|
|
@@index([clientId])
|
|
@@index([ownerId])
|
|
}
|
|
|
|
// Temporary authorization codes (10 min TTL)
|
|
model OAuthAuthorizationCode {
|
|
id String @id @default(uuid())
|
|
code String @unique
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime // Now + 10 minutes
|
|
|
|
applicationId String
|
|
Application OAuthApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
scopes APIKeyPermission[]
|
|
redirectUri String // Must match one from application
|
|
|
|
// PKCE (Proof Key for Code Exchange) support
|
|
codeChallenge String?
|
|
codeChallengeMethod String? // "S256" or "plain"
|
|
|
|
usedAt DateTime? // Set when code is consumed
|
|
|
|
@@index([code])
|
|
@@index([applicationId, userId])
|
|
@@index([expiresAt]) // For cleanup
|
|
}
|
|
|
|
// Access tokens (1 hour TTL)
|
|
model OAuthAccessToken {
|
|
id String @id @default(uuid())
|
|
token String @unique // SHA256 hash of plaintext token
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime // Now + 1 hour
|
|
|
|
applicationId String
|
|
Application OAuthApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
scopes APIKeyPermission[]
|
|
|
|
revokedAt DateTime? // Set when token is revoked
|
|
|
|
@@index([token]) // For token lookup
|
|
@@index([userId, applicationId])
|
|
@@index([expiresAt]) // For cleanup
|
|
}
|
|
|
|
// Refresh tokens (30 days TTL)
|
|
model OAuthRefreshToken {
|
|
id String @id @default(uuid())
|
|
token String @unique // SHA256 hash of plaintext token
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime // Now + 30 days
|
|
|
|
applicationId String
|
|
Application OAuthApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
scopes APIKeyPermission[]
|
|
|
|
revokedAt DateTime? // Set when token is revoked
|
|
|
|
@@index([token]) // For token lookup
|
|
@@index([userId, applicationId])
|
|
@@index([expiresAt]) // For cleanup
|
|
}
|