tpmjs/packages/db/prisma/schema.prisma
Ajax Davis 3456b26c9d feat(collections): add AI-generated use cases
Add "Example Use Cases" section to collection pages that generates
practical workflow examples showing how tools can work together.

- Add useCases and useCasesGeneratedAt fields to Collection model
- Create use-cases-generator.ts using Vercel AI SDK with gpt-4.1-mini
- Add POST /api/collections/[id]/use-cases/generate endpoint
- Add AI_GENERATION_RATE_LIMIT (5 req/hour per IP)
- Create UseCasesSection component with generate/regenerate UI
- Generate 6 use cases: 3 simple (1-2 tools) + 3 complex (3-5 tools)
- Include useCases in public collection API response
2026-01-16 02:33:57 +10:00

1120 lines
37 KiB
Text

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL") // Direct connection for migrations (bypasses pooler)
}
/// Package table - stores NPM package metadata (package-level)
model Package {
id String @id @default(cuid())
// NPM Metadata
npmPackageName String @unique @map("npm_package_name") @db.VarChar(214)
npmVersion String @map("npm_version") @db.VarChar(50)
npmPublishedAt DateTime @map("npm_published_at")
npmDescription String? @map("npm_description") @db.Text
npmRepository Json? @map("npm_repository") @db.JsonB
npmHomepage String? @map("npm_homepage") @db.Text
npmLicense String? @map("npm_license") @db.VarChar(50)
npmKeywords String[] @default([]) @map("npm_keywords") @db.Text
npmReadme String? @map("npm_readme") @db.Text
npmAuthor Json? @map("npm_author") @db.JsonB
npmMaintainers Json? @map("npm_maintainers") @db.JsonB
// TPMJS Package-Level Metadata
category String @db.VarChar(50)
env Json? @db.JsonB // Environment variables (shared across tools)
frameworks String[] @default([]) @db.Text
tier String @db.VarChar(20) // 'minimal' | 'rich'
discoveryMethod String @map("discovery_method") @db.VarChar(20) // 'keyword' | 'changes-feed'
isOfficial Boolean @default(false) @map("is_official")
// Package Metrics
npmDownloadsLastMonth Int? @default(0) @map("npm_downloads_last_month")
githubStars Int? @default(0) @map("github_stars")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
tools Tool[]
@@index([category])
@@index([isOfficial])
@@index([npmDownloadsLastMonth])
@@index([createdAt])
@@map("packages")
}
/// Tool table - stores individual tools within packages
model Tool {
id String @id @default(cuid())
// Package Relation
packageId String @map("package_id")
package Package @relation(fields: [packageId], references: [id], onDelete: Cascade)
// Tool Identity
name String @db.VarChar(100) // e.g., "helloWorldTool", "default"
// Tool Metadata
description String @db.Text
parameters Json? @db.JsonB // Legacy/fallback - author-provided parameters array
returns Json? @db.JsonB // @deprecated - will be auto-extracted in future
aiAgent Json? @map("ai_agent") @db.JsonB // @deprecated - will be auto-extracted in future
// Schema Extraction Fields
inputSchema Json? @map("input_schema") @db.JsonB // Full JSON Schema from executor
schemaSource String? @map("schema_source") @db.VarChar(20) // 'extracted' | 'author' | null
schemaExtractedAt DateTime? @map("schema_extracted_at") // Only updated on successful extraction
schemaExtractionAttemptAt DateTime? @map("schema_extraction_attempt_at") // Updated on every attempt (for rate limiting)
schemaExtractionError String? @map("schema_extraction_error") @db.Text // Error message from last failed attempt
// Tool Discovery Fields
toolDiscoverySource String? @map("tool_discovery_source") @db.VarChar(20) // 'auto' | 'manual' | null
// Tool Metrics
qualityScore Decimal? @map("quality_score") @db.Decimal(3, 2) // 0.00 to 1.00
likeCount Int @default(0) @map("like_count")
// Health Status Fields
importHealth HealthStatus? @default(UNKNOWN) @map("import_health")
executionHealth HealthStatus? @default(UNKNOWN) @map("execution_health")
lastHealthCheck DateTime? @map("last_health_check")
healthCheckError String? @map("health_check_error") @db.Text
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Rating aggregates
averageRating Decimal? @map("average_rating") @db.Decimal(2, 1) // 1.0 to 5.0
ratingCount Int @default(0) @map("rating_count")
reviewCount Int @default(0) @map("review_count")
// Relations
simulations Simulation[]
healthChecks HealthCheck[]
collections CollectionTool[]
agents AgentTool[]
likes ToolLike[]
ratings ToolRating[]
reviews ToolReview[]
@@unique([packageId, name])
@@index([qualityScore])
@@index([likeCount])
@@index([averageRating])
@@index([ratingCount])
@@index([reviewCount])
@@index([importHealth])
@@index([executionHealth])
@@index([lastHealthCheck])
@@map("tools")
}
/// Sync checkpoints - tracks progress of sync workers
model SyncCheckpoint {
id String @id @default(cuid())
source String @unique @db.VarChar(50) // 'changes-feed' | 'keyword-search' | 'metrics'
checkpoint Json @db.JsonB // Stores last sequence, timestamp, etc.
updatedAt DateTime @updatedAt @map("updated_at")
@@map("sync_checkpoints")
}
/// Sync logs - audit trail of sync operations
model SyncLog {
id String @id @default(cuid())
source String @db.VarChar(50) // 'changes-feed' | 'keyword-search' | 'metrics'
status String @db.VarChar(20) // 'success' | 'error' | 'partial'
processed Int @default(0) // Number of packages processed
skipped Int @default(0) // Number of packages skipped
errors Int @default(0) // Number of errors encountered
message String? @db.Text // Error message or summary
metadata Json? @db.JsonB // Additional context
createdAt DateTime @default(now()) @map("created_at")
@@index([source])
@@index([status])
@@index([createdAt])
@@map("sync_logs")
}
/// Simulations - tracks tool playground executions
model Simulation {
id String @id @default(cuid())
// Relations
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Request data
userPrompt String @map("user_prompt") @db.Text
parameters Json? @db.JsonB
ipAddress String? @map("ip_address") @db.VarChar(45)
userAgent String? @map("user_agent") @db.Text
// Results
status String @db.VarChar(20) // pending|running|success|error|timeout
executionTimeMs Int? @map("execution_time_ms")
output Json? @db.JsonB
error String? @db.Text
// AI metadata
agentSteps Int @default(0) @map("agent_steps")
model String? @db.VarChar(50)
// Relations
tokenUsage TokenUsage?
logs ExecutionLog[]
createdAt DateTime @default(now()) @map("created_at")
completedAt DateTime? @map("completed_at")
@@index([toolId])
@@index([status])
@@index([ipAddress, createdAt]) // For rate limiting
@@map("simulations")
}
/// Token usage - tracks token consumption per simulation
model TokenUsage {
id String @id @default(cuid())
simulationId String @unique @map("simulation_id")
simulation Simulation @relation(fields: [simulationId], references: [id], onDelete: Cascade)
inputTokens Int @default(0) @map("input_tokens")
toolDescTokens Int @default(0) @map("tool_desc_tokens")
schemaTokens Int @default(0) @map("schema_tokens")
outputTokens Int @default(0) @map("output_tokens")
totalTokens Int @default(0) @map("total_tokens")
estimatedCost Decimal? @map("estimated_cost") @db.Decimal(10, 6)
createdAt DateTime @default(now()) @map("created_at")
@@map("token_usage")
}
/// Execution logs - detailed logs for each simulation
model ExecutionLog {
id String @id @default(cuid())
simulationId String @map("simulation_id")
simulation Simulation @relation(fields: [simulationId], references: [id], onDelete: Cascade)
timestamp DateTime @default(now())
level String @db.VarChar(20) // info|warning|error|debug
event String @db.VarChar(50)
message String @db.Text
metadata Json? @db.JsonB
@@index([simulationId])
@@map("execution_logs")
}
/// HealthCheck table - tracks full audit history of health checks
model HealthCheck {
id String @id @default(cuid())
// Relations
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Check metadata
checkType HealthCheckType @map("check_type")
triggerSource String @map("trigger_source") @db.VarChar(50) // 'sync' | 'manual' | 'daily-cron'
// Import check results
importStatus HealthStatus @map("import_status")
importError String? @map("import_error") @db.Text
importTimeMs Int? @map("import_time_ms")
// Execution check results
executionStatus HealthStatus @map("execution_status")
executionError String? @map("execution_error") @db.Text
executionTimeMs Int? @map("execution_time_ms")
testParameters Json? @map("test_parameters") @db.JsonB
// Overall status
overallStatus HealthStatus @map("overall_status")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@index([toolId])
@@index([checkType])
@@index([overallStatus])
@@index([createdAt])
@@map("health_checks")
}
/// Health status enum - tracks health check results
enum HealthStatus {
UNKNOWN // Not yet checked
HEALTHY // Passed health check
BROKEN // Failed health check
}
/// Health check type enum - types of health checks performed
enum HealthCheckType {
IMPORT // Only import check
EXECUTION // Only execution check
FULL // Both import and execution
}
/// StatsSnapshot - daily snapshot of registry statistics for historical tracking
model StatsSnapshot {
id String @id @default(cuid())
// Snapshot date (one per day)
date DateTime @unique @db.Date
// Registry Overview
totalTools Int @default(0) @map("total_tools")
totalPackages Int @default(0) @map("total_packages")
officialTools Int @default(0) @map("official_tools")
officialPackages Int @default(0) @map("official_packages")
toolsWithSchema Int @default(0) @map("tools_with_schema")
// Downloads & Stars
totalNpmDownloads Int @default(0) @map("total_npm_downloads")
totalGithubStars Int @default(0) @map("total_github_stars")
// Health Status Counts
importHealthy Int @default(0) @map("import_healthy")
importBroken Int @default(0) @map("import_broken")
importUnknown Int @default(0) @map("import_unknown")
executionHealthy Int @default(0) @map("execution_healthy")
executionBroken Int @default(0) @map("execution_broken")
executionUnknown Int @default(0) @map("execution_unknown")
// Quality Distribution (stored as JSON for flexibility)
qualityDistribution Json? @map("quality_distribution") @db.JsonB
// Package Tiers
tiersMinimal Int @default(0) @map("tiers_minimal")
tiersRich Int @default(0) @map("tiers_rich")
// Execution Stats (daily)
executionsTotal Int @default(0) @map("executions_total")
executionsSuccessful Int @default(0) @map("executions_successful")
executionsFailed Int @default(0) @map("executions_failed")
executionsAvgTimeMs Int? @map("executions_avg_time_ms")
// Token Usage (daily)
tokensInput BigInt @default(0) @map("tokens_input")
tokensOutput BigInt @default(0) @map("tokens_output")
tokensTotal BigInt @default(0) @map("tokens_total")
tokensCostUsd Decimal? @map("tokens_cost_usd") @db.Decimal(10, 4)
// Health Checks (daily)
healthChecksRun Int @default(0) @map("health_checks_run")
// Category Breakdown (stored as JSON)
categories Json? @db.JsonB
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@index([date])
@@map("stats_snapshots")
}
// ============================================================================
// Better Auth Models
// ============================================================================
/// User table - stores authenticated users
model User {
id String @id @default(cuid())
name String
email String @unique
emailVerified Boolean @default(false) @map("email_verified")
image String?
username String? @unique @db.VarChar(30) // URL-friendly username (nullable for migration)
// Tier for rate limiting and feature access
tier UserTier @default(FREE)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
sessions Session[]
accounts Account[]
collections Collection[]
agents Agent[]
apiKeys UserApiKey[]
toolLikes ToolLike[]
toolRatings ToolRating[]
toolReviews ToolReview[]
collectionLikes CollectionLike[]
agentLikes AgentLike[]
activities UserActivity[]
bridgeConnection BridgeConnection?
tpmjsApiKeys TpmjsApiKey[] @relation("UserTpmjsApiKeys")
usageSummaries ApiUsageSummary[] @relation("UserUsageSummaries")
@@index([username])
@@map("users")
}
/// Session table - stores user sessions
model Session {
id String @id @default(cuid())
userId String @map("user_id")
token String @unique
expiresAt DateTime @map("expires_at")
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("sessions")
}
/// Account table - stores auth provider accounts (including email/password)
model Account {
id String @id @default(cuid())
userId String @map("user_id")
accountId String @map("account_id")
providerId String @map("provider_id")
accessToken String? @map("access_token") @db.Text
refreshToken String? @map("refresh_token") @db.Text
idToken String? @map("id_token") @db.Text
expiresAt DateTime? @map("expires_at")
password String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("accounts")
}
/// Verification table - stores email verification tokens
model Verification {
id String @id @default(cuid())
identifier String
value String
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("verifications")
}
// ============================================================================
// Collection Models
// ============================================================================
/// Collection - user-created groups of tools
model Collection {
id String @id @default(cuid())
// Owner relationship
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Collection metadata
name String @db.VarChar(100)
slug String? @db.VarChar(50) // URL-friendly identifier (nullable for migration)
description String? @db.VarChar(500)
isPublic Boolean @default(false) @map("is_public")
likeCount Int @default(0) @map("like_count")
// Executor configuration (optional - uses system default if not set)
executorType String? @map("executor_type") @db.VarChar(50)
executorConfig Json? @map("executor_config") @db.JsonB
// Tool environment variables (encrypted JSON: { "KEY": "value" })
// These are passed to tools when executed
envVars Json? @map("env_vars") @db.JsonB
// Fork tracking - for "fork to use" model
forkedFromId String? @map("forked_from_id")
forkedFrom Collection? @relation("CollectionForks", fields: [forkedFromId], references: [id], onDelete: SetNull)
forks Collection[] @relation("CollectionForks")
forkCount Int @default(0) @map("fork_count")
// AI-generated use cases
useCases Json? @map("use_cases") @db.JsonB
useCasesGeneratedAt DateTime? @map("use_cases_generated_at")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
tools CollectionTool[]
agents AgentCollection[]
likes CollectionLike[]
bridgeTools CollectionBridgeTool[]
// Unique constraint: user can't have duplicate collection slugs
@@unique([userId, slug])
@@index([userId])
@@index([slug])
@@index([isPublic])
@@index([likeCount])
@@index([createdAt])
@@index([forkedFromId])
@@index([forkCount])
@@map("collections")
}
/// CollectionTool - junction table for many-to-many relationship between collections and tools
model CollectionTool {
id String @id @default(cuid())
// Relationships
collectionId String @map("collection_id")
collection Collection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Ordering - allows users to reorder tools within a collection
position Int @default(0)
// Optional user notes about why this tool is in the collection
note String? @db.VarChar(500)
// Timestamps
addedAt DateTime @default(now()) @map("added_at")
// Unique constraint: tool can only be in a collection once
@@unique([collectionId, toolId])
@@index([collectionId])
@@index([toolId])
@@map("collection_tools")
}
// ============================================================================
// Agent Models
// ============================================================================
/// AI Provider enum - supported LLM providers
enum AIProvider {
OPENAI
ANTHROPIC
GOOGLE
GROQ
MISTRAL
}
/// Message role enum - conversation message types
enum MessageRole {
USER
ASSISTANT
TOOL
SYSTEM
}
/// Agent - user-owned AI agent configurations
model Agent {
id String @id @default(cuid())
// Owner relationship
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Agent identity
uid String @unique @db.VarChar(50) // URL-friendly identifier
name String @db.VarChar(100)
description String? @db.VarChar(500)
// Model configuration
provider AIProvider
modelId String @map("model_id") @db.VarChar(100)
systemPrompt String? @map("system_prompt") @db.Text
temperature Float @default(0.7)
maxToolCallsPerTurn Int @default(20) @map("max_tool_calls_per_turn")
maxMessagesInContext Int @default(10) @map("max_messages_in_context")
// Visibility
isPublic Boolean @default(true) @map("is_public")
likeCount Int @default(0) @map("like_count")
// Executor configuration (optional - uses system default if not set)
// Agent executor overrides collection executor overrides system default
executorType String? @map("executor_type") @db.VarChar(50)
executorConfig Json? @map("executor_config") @db.JsonB
// Tool environment variables (encrypted JSON: { "KEY": "value" })
// Agent env vars override collection env vars, otherwise merged
envVars Json? @map("env_vars") @db.JsonB
// Fork tracking - for "fork to use" model
forkedFromId String? @map("forked_from_id")
forkedFrom Agent? @relation("AgentForks", fields: [forkedFromId], references: [id], onDelete: SetNull)
forks Agent[] @relation("AgentForks")
forkCount Int @default(0) @map("fork_count")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
collections AgentCollection[]
tools AgentTool[]
conversations Conversation[]
likes AgentLike[]
@@unique([userId, name])
@@index([userId])
@@index([uid])
@@index([isPublic])
@@index([likeCount])
@@index([createdAt])
@@index([forkedFromId])
@@index([forkCount])
@@map("agents")
}
/// AgentCollection - junction table for Agent -> Collection (many-to-many)
model AgentCollection {
id String @id @default(cuid())
// Relationships
agentId String @map("agent_id")
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
collectionId String @map("collection_id")
collection Collection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
// Ordering
position Int @default(0)
// Timestamps
addedAt DateTime @default(now()) @map("added_at")
@@unique([agentId, collectionId])
@@index([agentId])
@@index([collectionId])
@@map("agent_collections")
}
/// AgentTool - junction table for Agent -> Tool (many-to-many)
model AgentTool {
id String @id @default(cuid())
// Relationships
agentId String @map("agent_id")
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Ordering
position Int @default(0)
// Timestamps
addedAt DateTime @default(now()) @map("added_at")
@@unique([agentId, toolId])
@@index([agentId])
@@index([toolId])
@@map("agent_tools")
}
/// UserApiKey - encrypted API keys/env vars per user
model UserApiKey {
id String @id @default(cuid())
// Owner relationship
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Key identification (e.g. OPENAI_API_KEY, MY_SECRET, etc.)
keyName String @map("key_name") @db.VarChar(100)
// Encrypted key storage
encryptedKey String @map("encrypted_key") @db.Text
keyIv String @map("key_iv") @db.VarChar(32)
keyHint String? @map("key_hint") @db.VarChar(10) // Last 4 chars
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userId, keyName])
@@index([userId])
@@map("user_api_keys")
}
/// Conversation - chat session with an agent
model Conversation {
id String @id @default(cuid())
// Agent relationship
agentId String @map("agent_id")
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
// Conversation identity
slug String @db.VarChar(100) // User-chosen unique ID
// Metadata
title String? @db.VarChar(200)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
messages Message[]
@@unique([agentId, slug])
@@index([agentId])
@@index([createdAt])
@@map("conversations")
}
/// Message - individual message in a conversation
model Message {
id String @id @default(cuid())
// Conversation relationship
conversationId String @map("conversation_id")
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
// Message content
role MessageRole
content String @db.Text
// Tool call metadata (for role=ASSISTANT with tool calls)
toolCalls Json? @map("tool_calls") @db.JsonB
// Tool result metadata (for role=TOOL)
toolCallId String? @map("tool_call_id") @db.VarChar(100)
toolName String? @map("tool_name") @db.VarChar(200)
toolResult Json? @map("tool_result") @db.JsonB
// Token tracking
inputTokens Int? @map("input_tokens")
outputTokens Int? @map("output_tokens")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@index([conversationId])
@@index([createdAt])
@@map("messages")
}
// ============================================================================
// Like Models
// ============================================================================
/// ToolLike - tracks users who liked a tool
model ToolLike {
id String @id @default(cuid())
// Relationships
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@unique([userId, toolId])
@@index([toolId])
@@index([userId])
@@map("tool_likes")
}
/// CollectionLike - tracks users who liked a collection
model CollectionLike {
id String @id @default(cuid())
// Relationships
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
collectionId String @map("collection_id")
collection Collection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@unique([userId, collectionId])
@@index([collectionId])
@@index([userId])
@@map("collection_likes")
}
/// AgentLike - tracks users who liked an agent
model AgentLike {
id String @id @default(cuid())
// Relationships
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
agentId String @map("agent_id")
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@unique([userId, agentId])
@@index([agentId])
@@index([userId])
@@map("agent_likes")
}
/// ToolRating - user ratings for tools (1-5 stars)
model ToolRating {
id String @id @default(cuid())
// Relationships
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Rating value (1-5)
rating Int @db.SmallInt
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userId, toolId])
@@index([toolId])
@@index([userId])
@@index([rating])
@@map("tool_ratings")
}
/// ToolReview - user reviews for tools
model ToolReview {
id String @id @default(cuid())
// Relationships
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
toolId String @map("tool_id")
tool Tool @relation(fields: [toolId], references: [id], onDelete: Cascade)
// Review content
title String? @db.VarChar(200)
content String @db.Text
rating Int @db.SmallInt // 1-5, denormalized for convenience
// Moderation
isApproved Boolean @default(true) @map("is_approved")
isHidden Boolean @default(false) @map("is_hidden")
// Helpful votes
helpfulCount Int @default(0) @map("helpful_count")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userId, toolId])
@@index([toolId])
@@index([userId])
@@index([rating])
@@index([isApproved, isHidden])
@@index([createdAt])
@@map("tool_reviews")
}
// ============================================================================
// Activity Stream Models
// ============================================================================
/// Activity type enum - types of user activities tracked
enum ActivityType {
AGENT_CREATED
AGENT_UPDATED
AGENT_DELETED
AGENT_CLONED
AGENT_FORKED
AGENT_TOOL_ADDED
AGENT_TOOL_REMOVED
AGENT_COLLECTION_ADDED
AGENT_COLLECTION_REMOVED
COLLECTION_CREATED
COLLECTION_UPDATED
COLLECTION_DELETED
COLLECTION_CLONED
COLLECTION_FORKED
COLLECTION_TOOL_ADDED
COLLECTION_TOOL_REMOVED
TOOL_LIKED
TOOL_UNLIKED
COLLECTION_LIKED
COLLECTION_UNLIKED
AGENT_LIKED
AGENT_UNLIKED
}
/// UserActivity - tracks user actions for activity stream
model UserActivity {
id String @id @default(cuid())
// Owner relationship
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Activity type
type ActivityType
// Optional entity references (for linking to entities if they still exist)
agentId String? @map("agent_id")
collectionId String? @map("collection_id")
toolId String? @map("tool_id")
// Denormalized fields (stored at creation time for display even after entity deletion)
targetName String @map("target_name") @db.VarChar(200)
targetType String @map("target_type") @db.VarChar(50) // 'agent' | 'collection' | 'tool'
// Additional context (e.g., toolName when adding to collection)
metadata Json? @db.JsonB
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@index([userId])
@@index([userId, createdAt])
@@index([type])
@@index([createdAt])
@@map("user_activities")
}
// ============================================================================
// Endpoint Health Monitoring Models
// ============================================================================
/// EndpointHealthReport - stores health check results from GitHub Actions or other monitoring
model EndpointHealthReport {
id String @id @default(cuid())
// Report metadata
timestamp DateTime
source String @db.VarChar(50) // 'github-actions' | 'manual' | 'uptime-robot' etc.
runId String? @map("run_id") @db.VarChar(100) // GitHub Actions run ID
// Check results
checks Json @db.JsonB // { check_name: 'pass' | 'fail' }
passCount Int @map("pass_count")
failCount Int @map("fail_count")
totalChecks Int @map("total_checks")
overallStatus String @map("overall_status") @db.VarChar(20) // 'healthy' | 'degraded' | 'down'
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@index([timestamp])
@@index([overallStatus])
@@index([source])
@@map("endpoint_health_reports")
}
// ============================================================================
// MCP Bridge Models
// ============================================================================
/// BridgeConnection - tracks active bridge connections from users
model BridgeConnection {
id String @id @default(cuid())
// Owner relationship
userId String @unique @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Connection state
status String @default("disconnected") @db.VarChar(20) // 'connected' | 'disconnected'
socketId String? @map("socket_id") @db.VarChar(100) // Internal socket identifier for routing
// Cached tool definitions from bridge
tools Json @default("[]") @db.JsonB
// Metadata
lastSeen DateTime? @map("last_seen")
clientVersion String? @map("client_version") @db.VarChar(20)
clientOS String? @map("client_os") @db.VarChar(50)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([status])
@@map("bridge_connections")
}
/// CollectionBridgeTool - tracks which bridge tools are added to collections
model CollectionBridgeTool {
id String @id @default(cuid())
// Collection relationship
collectionId String @map("collection_id")
collection Collection @relation(fields: [collectionId], references: [id], onDelete: Cascade)
// Bridge tool reference
serverId String @map("server_id") @db.VarChar(100) // e.g., "chrome-devtools"
toolName String @map("tool_name") @db.VarChar(100) // e.g., "screenshot"
// Display customization
displayName String? @map("display_name") @db.VarChar(100) // Override tool name in MCP
note String? @db.VarChar(500) // User notes
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([collectionId, serverId, toolName])
@@index([collectionId])
@@map("collection_bridge_tools")
}
// ============================================================================
// API Key & Usage Tracking Models
// ============================================================================
/// User tier enum - determines rate limits and feature access
enum UserTier {
FREE
PRO
ENTERPRISE
}
/// TpmjsApiKey - user-owned API keys for programmatic access
model TpmjsApiKey {
id String @id @default(cuid())
// Owner relationship
userId String @map("user_id")
user User @relation("UserTpmjsApiKeys", fields: [userId], references: [id], onDelete: Cascade)
// Key identification
name String @db.VarChar(100) // User-provided name (e.g., "Production Server")
keyHash String @unique @map("key_hash") @db.VarChar(64) // SHA-256 hash (never store raw keys)
keyPrefix String @map("key_prefix") @db.VarChar(20) // First 16 chars for identification (tpmjs_sk_abc123...)
// Permissions
scopes String[] @default([]) // ["mcp:execute", "agent:chat", "bridge:connect", "usage:read"]
// Rate limiting (overrides tier default if set)
rateLimit Int? @map("rate_limit") // Requests per hour (null = use tier default)
// Status
isActive Boolean @default(true) @map("is_active")
lastUsedAt DateTime? @map("last_used_at")
expiresAt DateTime? @map("expires_at")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relations
usageRecords ApiUsageRecord[]
@@index([userId])
@@index([keyHash])
@@index([keyPrefix])
@@index([isActive])
@@map("tpmjs_api_keys")
}
/// ApiUsageRecord - individual API request logs (kept for 30 days)
model ApiUsageRecord {
id String @id @default(cuid())
// API key relationship
apiKeyId String @map("api_key_id")
apiKey TpmjsApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
// Request details
endpoint String @db.VarChar(500)
method String @db.VarChar(10) // GET, POST, etc.
statusCode Int @map("status_code")
latencyMs Int @map("latency_ms")
// Resource tracking
resourceType String? @map("resource_type") @db.VarChar(50) // "mcp" | "agent" | "bridge" | "collection"
resourceId String? @map("resource_id") @db.VarChar(100) // Collection ID, Agent ID, etc.
// LLM usage (if applicable)
tokensIn Int? @map("tokens_in")
tokensOut Int? @map("tokens_out")
model String? @db.VarChar(50) // e.g., "gpt-4o-mini"
// Error tracking
errorCode String? @map("error_code") @db.VarChar(50)
errorMessage String? @map("error_message") @db.Text
// Client metadata
userAgent String? @map("user_agent") @db.VarChar(500)
ipAddress String? @map("ip_address") @db.VarChar(45) // IPv4 or IPv6
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
@@index([apiKeyId])
@@index([createdAt])
@@index([endpoint])
@@index([resourceType, resourceId])
@@map("api_usage_records")
}
/// ApiUsageSummary - aggregated usage summaries (hourly/daily rollups)
model ApiUsageSummary {
id String @id @default(cuid())
// User relationship
userId String @map("user_id")
user User @relation("UserUsageSummaries", fields: [userId], references: [id], onDelete: Cascade)
// Optional API key (null for user-level summaries)
apiKeyId String? @map("api_key_id")
// Time period
periodType String @map("period_type") @db.VarChar(20) // "hourly" | "daily" | "monthly"
periodStart DateTime @map("period_start")
// Request counts
totalRequests Int @default(0) @map("total_requests")
successRequests Int @default(0) @map("success_requests")
errorRequests Int @default(0) @map("error_requests")
// Endpoint breakdown (JSON: { "/api/mcp/...": 100, ... })
endpointCounts Json @default("{}") @map("endpoint_counts") @db.JsonB
// LLM usage totals
totalTokensIn Int @default(0) @map("total_tokens_in")
totalTokensOut Int @default(0) @map("total_tokens_out")
// Performance
avgLatencyMs Float @default(0) @map("avg_latency_ms")
p95LatencyMs Float @default(0) @map("p95_latency_ms")
// Cost estimation (in cents)
estimatedCostCents Int @default(0) @map("estimated_cost_cents")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userId, apiKeyId, periodType, periodStart])
@@index([userId])
@@index([periodType, periodStart])
@@map("api_usage_summaries")
}