feat(#2): Implement PostgreSQL 17 + pgvector database schema
Establishes multi-tenant database layer with vector similarity search for AI-powered memory features. Includes Docker infrastructure, Prisma ORM integration, NestJS services, and shared types across the monorepo. Key changes: - Docker: PostgreSQL 17 + pgvector v0.7.4, Valkey cache - Schema: 8 models (User, Workspace, Task, Event, Project, ActivityLog, MemoryEmbedding) with RLS preparation - NestJS: PrismaModule, DatabaseModule, EmbeddingsService - Shared: Type-safe enums, constants, and database types Fixes #2 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
253
apps/api/prisma/schema.prisma
Normal file
253
apps/api/prisma/schema.prisma
Normal file
@@ -0,0 +1,253 @@
|
||||
// Mosaic Stack Database Schema
|
||||
// PostgreSQL 17 with pgvector extension
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["postgresqlExtensions"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
extensions = [pgvector(map: "vector"), uuid_ossp(map: "uuid-ossp")]
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ENUMS
|
||||
// ============================================
|
||||
|
||||
enum TaskStatus {
|
||||
NOT_STARTED
|
||||
IN_PROGRESS
|
||||
PAUSED
|
||||
COMPLETED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
enum TaskPriority {
|
||||
LOW
|
||||
MEDIUM
|
||||
HIGH
|
||||
}
|
||||
|
||||
enum ProjectStatus {
|
||||
PLANNING
|
||||
ACTIVE
|
||||
PAUSED
|
||||
COMPLETED
|
||||
ARCHIVED
|
||||
}
|
||||
|
||||
enum WorkspaceMemberRole {
|
||||
OWNER
|
||||
ADMIN
|
||||
MEMBER
|
||||
GUEST
|
||||
}
|
||||
|
||||
enum ActivityAction {
|
||||
CREATED
|
||||
UPDATED
|
||||
DELETED
|
||||
COMPLETED
|
||||
ASSIGNED
|
||||
COMMENTED
|
||||
}
|
||||
|
||||
enum EntityType {
|
||||
TASK
|
||||
EVENT
|
||||
PROJECT
|
||||
WORKSPACE
|
||||
USER
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// MODELS
|
||||
// ============================================
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
email String @unique
|
||||
name String
|
||||
authProviderId String? @unique @map("auth_provider_id")
|
||||
preferences Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
ownedWorkspaces Workspace[] @relation("WorkspaceOwner")
|
||||
workspaceMemberships WorkspaceMember[]
|
||||
assignedTasks Task[] @relation("TaskAssignee")
|
||||
createdTasks Task[] @relation("TaskCreator")
|
||||
createdEvents Event[] @relation("EventCreator")
|
||||
createdProjects Project[] @relation("ProjectCreator")
|
||||
activityLogs ActivityLog[]
|
||||
|
||||
@@map("users")
|
||||
}
|
||||
|
||||
model Workspace {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
name String
|
||||
ownerId String @map("owner_id") @db.Uuid
|
||||
settings Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
owner User @relation("WorkspaceOwner", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
members WorkspaceMember[]
|
||||
tasks Task[]
|
||||
events Event[]
|
||||
projects Project[]
|
||||
activityLogs ActivityLog[]
|
||||
memoryEmbeddings MemoryEmbedding[]
|
||||
|
||||
@@index([ownerId])
|
||||
@@map("workspaces")
|
||||
}
|
||||
|
||||
model WorkspaceMember {
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
role WorkspaceMemberRole @default(MEMBER)
|
||||
joinedAt DateTime @default(now()) @map("joined_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([workspaceId, userId])
|
||||
@@index([userId])
|
||||
@@map("workspace_members")
|
||||
}
|
||||
|
||||
model Task {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
title String
|
||||
description String? @db.Text
|
||||
status TaskStatus @default(NOT_STARTED)
|
||||
priority TaskPriority @default(MEDIUM)
|
||||
dueDate DateTime? @map("due_date") @db.Timestamptz
|
||||
assigneeId String? @map("assignee_id") @db.Uuid
|
||||
creatorId String @map("creator_id") @db.Uuid
|
||||
projectId String? @map("project_id") @db.Uuid
|
||||
parentId String? @map("parent_id") @db.Uuid
|
||||
sortOrder Int @default(0) @map("sort_order")
|
||||
metadata Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
completedAt DateTime? @map("completed_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
assignee User? @relation("TaskAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
|
||||
creator User @relation("TaskCreator", fields: [creatorId], references: [id], onDelete: Cascade)
|
||||
project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull)
|
||||
parent Task? @relation("TaskSubtasks", fields: [parentId], references: [id], onDelete: Cascade)
|
||||
subtasks Task[] @relation("TaskSubtasks")
|
||||
|
||||
@@index([workspaceId])
|
||||
@@index([workspaceId, status])
|
||||
@@index([workspaceId, dueDate])
|
||||
@@index([assigneeId])
|
||||
@@index([projectId])
|
||||
@@index([parentId])
|
||||
@@map("tasks")
|
||||
}
|
||||
|
||||
model Event {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
title String
|
||||
description String? @db.Text
|
||||
startTime DateTime @map("start_time") @db.Timestamptz
|
||||
endTime DateTime? @map("end_time") @db.Timestamptz
|
||||
allDay Boolean @default(false) @map("all_day")
|
||||
location String?
|
||||
recurrence Json?
|
||||
creatorId String @map("creator_id") @db.Uuid
|
||||
projectId String? @map("project_id") @db.Uuid
|
||||
metadata Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
creator User @relation("EventCreator", fields: [creatorId], references: [id], onDelete: Cascade)
|
||||
project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@index([workspaceId])
|
||||
@@index([workspaceId, startTime])
|
||||
@@index([creatorId])
|
||||
@@index([projectId])
|
||||
@@map("events")
|
||||
}
|
||||
|
||||
model Project {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
name String
|
||||
description String? @db.Text
|
||||
status ProjectStatus @default(PLANNING)
|
||||
startDate DateTime? @map("start_date") @db.Date
|
||||
endDate DateTime? @map("end_date") @db.Date
|
||||
creatorId String @map("creator_id") @db.Uuid
|
||||
color String?
|
||||
metadata Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
creator User @relation("ProjectCreator", fields: [creatorId], references: [id], onDelete: Cascade)
|
||||
tasks Task[]
|
||||
events Event[]
|
||||
|
||||
@@index([workspaceId])
|
||||
@@index([workspaceId, status])
|
||||
@@index([creatorId])
|
||||
@@map("projects")
|
||||
}
|
||||
|
||||
model ActivityLog {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
userId String @map("user_id") @db.Uuid
|
||||
action ActivityAction
|
||||
entityType EntityType @map("entity_type")
|
||||
entityId String @map("entity_id") @db.Uuid
|
||||
details Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([workspaceId])
|
||||
@@index([workspaceId, createdAt])
|
||||
@@index([entityType, entityId])
|
||||
@@index([userId])
|
||||
@@map("activity_logs")
|
||||
}
|
||||
|
||||
model MemoryEmbedding {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
workspaceId String @map("workspace_id") @db.Uuid
|
||||
content String @db.Text
|
||||
// Note: vector dimension (1536) must match EMBEDDING_DIMENSION constant in @mosaic/shared
|
||||
embedding Unsupported("vector(1536)")?
|
||||
entityType EntityType? @map("entity_type")
|
||||
entityId String? @map("entity_id") @db.Uuid
|
||||
metadata Json @default("{}")
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
||||
|
||||
// Relations
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([workspaceId])
|
||||
@@map("memory_embeddings")
|
||||
}
|
||||
Reference in New Issue
Block a user