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
305 lines
8.8 KiB
Plaintext
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")
|
|
}
|