YAS/packages/db/prisma/schema.prisma
YetAnotherSuite Dev 262fbf48e2 feat: Phase 1 foundation — monorepo, auth, DB schema, API, shared packages
Phase 1.1: Monorepo Setup & Design System
- Turborepo + pnpm workspaces monorepo structure
- Shared packages: @yetanother/types, @yetanother/utils, @yetanother/hooks, @yetanother/ui, @yetanother/db
- Radix UI primitives with custom shadcn/ui-style components (Button, Dialog, Toast, Tooltip, Tabs, Label)
- Tailwind CSS v4 with design tokens (light theme)
- TypeScript strict mode with composite project references

Phase 1.2: Authentication & User Management
- Fastify server setup with JWT auth (register, login, me endpoints)
- @fastify/jwt with cookie support
- User and workspace models with Prisma ORM

Phase 1.3: Database Schema & Migrations
- Full Prisma schema: User, Workspace, Node, NodeLink, Tag,
  Folder, Reminder, Embedding, Activity, Attachment,
  WorkspaceMember, SyncCheckpoint
- pgvector extension for embeddings (vector(1536))
- Seed script for dev data
- Comprehensive indexes (GIN, BRIN, B-tree)

Phase 1.4: API Foundation & Middleware
- Fastify REST API with versioned routes (/api/v1)
- Zod request validation on all endpoints
- CORS, Helmet security headers, rate limiting
- Structured logging with pino-pretty
- Swagger/OpenAPI docs at /docs
- WebSocket support (collaboration ready)
- CRUD routes: nodes, workspaces, search, auth

Tooling:
- ESLint 9 flat config with TypeScript parser
- Prettier with consistent formatting rules
- Turbo v2 task orchestration
- GitHub Actions CI workflow
- Husky + lint-staged pre-commit hooks
2026-07-20 21:58:17 +02:00

305 lines
8.8 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum NodeType {
task
event
note
project
goal
}
enum NodeStatus {
active
archived
deleted
}
enum LinkType {
references
blocks
relates_to
parent_of
child_of
scheduled_as
prepared_for
}
enum WorkspaceRole {
owner
admin
editor
viewer
}
enum ReminderStatus {
pending
sent
dismissed
snoozed
}
enum NotificationType {
push
email
sms
in_app
}
enum ActionType {
created
updated
deleted
viewed
shared
linked
completed
ai_assisted
}
model User {
id String @id @default(uuid()) @db.Uuid
email String @unique
displayName String @map("display_name")
avatarUrl String? @map("avatar_url") @db.VarChar
preferences Json @default("{}")
encryptionKey String? @map("encryption_key")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lastActiveAt DateTime @default(now()) @map("last_active_at")
nodes Node[]
ownedWorkspaces Workspace[] @relation("OwnedWorkspaces")
memberships WorkspaceMember[]
activities Activity[]
reminders Reminder[]
syncCheckpoints SyncCheckpoint[]
@@map("users")
}
model Workspace {
id String @id @default(uuid()) @db.Uuid
name String
slug String
description String?
settings Json @default("{}")
ownerId String @map("owner_id") @db.Uuid
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
owner User @relation("OwnedWorkspaces", fields: [ownerId], references: [id])
members WorkspaceMember[]
nodes Node[]
tags Tag[]
folders Folder[]
@@unique([slug, ownerId])
@@map("workspaces")
}
model Node {
id String @id @default(uuid()) @db.Uuid
type NodeType
workspaceId String @map("workspace_id") @db.Uuid
ownerId String @map("owner_id") @db.Uuid
parentId String? @map("parent_id") @db.Uuid
title String
content Json @default("{}")
plainText String @default("") @map("plain_text")
status NodeStatus @default(active)
priority Int?
startTime DateTime? @map("start_time")
endTime DateTime? @map("end_time")
durationMinutes Int? @map("duration_minutes")
recurrenceRule String? @map("recurrence_rule")
completionRate Float? @map("completion_rate")
metadata Json @default("{}")
version Int @default(1)
isEncrypted Boolean @default(false) @map("is_encrypted")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
completedAt DateTime? @map("completed_at")
archivedAt DateTime? @map("archived_at")
workspace Workspace @relation(fields: [workspaceId], references: [id])
owner User @relation(fields: [ownerId], references: [id])
parent Node? @relation("NodeHierarchy", fields: [parentId], references: [id])
children Node[] @relation("NodeHierarchy")
tags NodeTag[]
links NodeLink[] @relation("NodeLinksSource")
backlinks NodeLink[] @relation("NodeLinksTarget")
activities Activity[]
reminders Reminder[]
attachments Attachment[]
embeddings Embedding[]
@@index([type, status, workspaceId, ownerId])
@@index([createdAt])
@@map("nodes")
}
model NodeLink {
id String @id @default(uuid()) @db.Uuid
sourceId String @map("source_id") @db.Uuid
targetId String @map("target_id") @db.Uuid
linkType LinkType @map("link_type")
strength Float @default(0.5)
isAuto Boolean @default(false) @map("is_auto")
context String? @db.Text
createdAt DateTime @default(now()) @map("created_at")
source Node @relation("NodeLinksSource", fields: [sourceId], references: [id])
target Node @relation("NodeLinksTarget", fields: [targetId], references: [id])
@@unique([sourceId, targetId, linkType])
@@index([sourceId])
@@index([targetId])
@@map("node_links")
}
model Tag {
id String @id @default(uuid()) @db.Uuid
name String
color String @db.VarChar(7)
workspaceId String @map("workspace_id") @db.Uuid
isSystem Boolean @default(false) @map("is_system")
createdAt DateTime @default(now()) @map("created_at")
workspace Workspace @relation(fields: [workspaceId], references: [id])
nodes NodeTag[]
@@map("tags")
}
model NodeTag {
nodeId String @map("node_id") @db.Uuid
tagId String @map("tag_id") @db.Uuid
node Node @relation(fields: [nodeId], references: [id])
tag Tag @relation(fields: [tagId], references: [id])
@@id([nodeId, tagId])
@@map("node_tags")
}
model Folder {
id String @id @default(uuid()) @db.Uuid
name String
workspaceId String @map("workspace_id") @db.Uuid
parentId String? @map("parent_id") @db.Uuid
viewType String @default("list") @map("view_type")
filterConfig Json @default("{}") @map("filter_config")
sortConfig Json @default("{}") @map("sort_config")
orderIndex Int @default(0) @map("order_index")
createdAt DateTime @default(now()) @map("created_at")
workspace Workspace @relation(fields: [workspaceId], references: [id])
parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id])
children Folder[] @relation("FolderHierarchy")
@@map("folders")
}
model Reminder {
id String @id @default(uuid()) @db.Uuid
nodeId String @map("node_id") @db.Uuid
userId String @map("user_id") @db.Uuid
remindAt DateTime @map("remind_at")
notificationType NotificationType @map("notification_type")
status ReminderStatus @default(pending)
snoozeUntil DateTime? @map("snooze_until")
createdAt DateTime @default(now()) @map("created_at")
node Node @relation(fields: [nodeId], references: [id])
user User @relation(fields: [userId], references: [id])
@@map("reminders")
}
model Embedding {
id String @id @default(uuid()) @db.Uuid
nodeId String @map("node_id") @db.Uuid
chunkIndex Int @map("chunk_index")
chunkText String @map("chunk_text") @db.Text
vector Unsupported("vector(1536)")
modelVersion String @map("model_version")
createdAt DateTime @default(now()) @map("created_at")
node Node @relation(fields: [nodeId], references: [id])
@@index([nodeId])
@@map("embeddings")
}
model Activity {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
nodeId String? @map("node_id") @db.Uuid
workspaceId String @map("workspace_id") @db.Uuid
actionType ActionType @map("action_type")
metadata Json @default("{}")
sessionId String? @map("session_id") @db.Text
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id])
node Node? @relation(fields: [nodeId], references: [id])
@@index([createdAt])
@@index([userId, workspaceId, actionType])
@@map("activities")
}
model Attachment {
id String @id @default(uuid()) @db.Uuid
nodeId String @map("node_id") @db.Uuid
filename String
mimeType String @map("mime_type")
sizeBytes Int @map("size_bytes")
storageKey String @map("storage_key")
thumbnailKey String? @map("thumbnail_key")
uploadedBy String @map("uploaded_by") @db.Uuid
createdAt DateTime @default(now()) @map("created_at")
node Node @relation(fields: [nodeId], references: [id])
@@map("attachments")
}
model WorkspaceMember {
id String @id @default(uuid()) @db.Uuid
workspaceId String @map("workspace_id") @db.Uuid
userId String @map("user_id") @db.Uuid
role WorkspaceRole
permissions Json @default("{}")
joinedAt DateTime @default(now()) @map("joined_at")
lastAccessedAt DateTime @default(now()) @map("last_accessed_at")
workspace Workspace @relation(fields: [workspaceId], references: [id])
user User @relation(fields: [userId], references: [id])
@@unique([workspaceId, userId])
@@map("workspace_members")
}
model SyncCheckpoint {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
deviceId String @map("device_id")
lastSyncAt DateTime @default(now()) @map("last_sync_at")
syncToken String? @map("sync_token")
deviceInfo Json @default("{}") @map("device_info")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id])
@@unique([userId, deviceId])
@@map("sync_checkpoints")
}