# Data Models - Memento Project

## Overview

Memento uses SQLite as its database with Prisma ORM. The database schema is shared between the web application (keep-notes) and the MCP server.

**Database:** SQLite (`prisma/dev.db`)
**ORM:** Prisma 5.22.0
**Adapters:**
- `@prisma/adapter-better-sqlite3` (primary)
- `@prisma/adapter-libsql` (alternative for Turso)

---

## Database Schema

### User

Represents a user account with authentication and profile information.

```prisma
model User {
  id               String     @id @default(cuid())
  name             String?
  email            String     @unique
  emailVerified    DateTime?
  password         String?    // Hashed password (bcrypt)
  role             String     @default("USER")  // "USER" or "ADMIN"
  image            String?    // Profile picture URL
  theme            String     @default("light") // UI theme preference
  resetToken       String?    @unique           // Password reset token
  resetTokenExpiry DateTime?                    // Reset token expiration
  accounts         Account[]
  sessions         Session[]
  notes            Note[]
  labels           Label[]
  createdAt        DateTime   @default(now())
  updatedAt        DateTime   @updatedAt
}
```

**Relationships:**
- One-to-many with Account (OAuth providers)
- One-to-many with Session (active sessions)
- One-to-many with Note (user's notes)
- One-to-many with Label (user's labels)

**Fields:**
- `id`: CUID (Collision-resistant Unique Identifier)
- `email`: Unique email address
- `password`: Optional (can be OAuth-only users)
- `role`: RBAC - "USER" or "ADMIN"
- `theme`: UI theme preference ("light" or "dark")
- `resetToken`: Password recovery token (unique)
- `resetTokenExpiry`: Token validity period

---

### Account

Stores OAuth provider account information (NextAuth.js).

```prisma
model Account {
  userId                String
  type                  String
  provider              String           // google, github, etc.
  providerAccountId     String
  refresh_token         String?
  access_token          String?
  expires_at            Int?
  token_type            String?
  scope                 String?
  id_token              String?
  session_state         String?
  createdAt             DateTime         @default(now())
  updatedAt             DateTime         @updatedAt
  user                  User             @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@id([provider, providerAccountId])
}
```

**Purpose:** Links OAuth provider accounts to local User accounts

**Relationships:**
- Many-to-one with User (via `userId`)

**Constraints:**
- Composite unique key on `provider` + `providerAccountId`

---

### Session

Stores active user sessions (NextAuth.js).

```prisma
model Session {
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
```

**Purpose:** Manages active user sessions for authentication

**Relationships:**
- Many-to-one with User (via `userId`)

**Fields:**
- `sessionToken`: Unique session identifier
- `expires`: Session expiration timestamp

---

### VerificationToken

Stores email verification tokens (NextAuth.js).

```prisma
model VerificationToken {
  identifier String
  token      String
  expires    DateTime

  @@id([identifier, token])
}
```

**Purpose:** Email verification flow

**Constraints:**
- Composite unique key on `identifier` + `token`

---

### Label

User-defined labels/tags for organizing notes.

```prisma
model Label {
  id        String   @id @default(cuid())
  name      String
  color     String   @default("gray")
  userId    String?  // Made optional for migration
  user      User?    @relation(fields: [userId], references: [id], onDelete: Cascade)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@unique([name, userId])  // Labels unique per user
  @@index([userId])
}
```

**Purpose:** Categorization and organization of notes

**Relationships:**
- Many-to-one with User (via `userId`)

**Fields:**
- `name`: Label display name
- `color`: Visual color (red, orange, yellow, green, teal, blue, purple, pink, gray)
- `userId`: Optional (migration artifact, logic enforces ownership)

**Constraints:**
- Unique label name per user
- Indexed on `userId` for fast lookup

**Usage:**
- Stored as JSON array in `Note.labels`
- User can have multiple labels with the same name? No, unique constraint

---

### Note

Core data model - represents a note in the system.

```prisma
model Note {
  id                  String    @id @default(cuid())
  title               String?              // Optional title
  content             String
  color               String    @default("default")
  isPinned            Boolean   @default(false)
  isArchived          Boolean   @default(false)
  type                String    @default("text")         // "text" or "checklist"
  checkItems          String?                         // JSON array
  labels              String?                         // JSON array
  images              String?                         // JSON array
  links               String?                         // JSON array
  reminder            DateTime?                       // Reminder timestamp
  isReminderDone      Boolean   @default(false)
  reminderRecurrence  String?                         // "none", "daily", "weekly", "monthly", "custom"
  reminderLocation    String?                         // Location-based reminders
  isMarkdown          Boolean   @default(false)       // Markdown rendering
  size                String    @default("small")      // "small", "medium", "large"
  embedding           String?                         // Vector embeddings (JSON)
  userId              String?                         // Owner
  user                User?     @relation(fields: [userId], references: [id], onDelete: Cascade)
  order               Int       @default(0)
  createdAt           DateTime  @default(now())
  updatedAt           DateTime  @updatedAt

  @@index([isPinned])
  @@index([isArchived])
  @@index([order])
  @@index([reminder])
  @@index([userId])
}
```

**Purpose:** Core note-taking entity

**Relationships:**
- Many-to-one with User (via `userId`)

**Fields:**

**Basic:**
- `id`: CUID
- `title`: Optional heading/title
- `content`: Note content (text or markdown)
- `color`: Background color (default, red, orange, yellow, green, teal, blue, purple, pink, gray)
- `type`: Note type - "text" or "checklist"

**State Flags:**
- `isPinned`: Shows at top of list
- `isArchived**: Removed from main view
- `isReminderDone`: Reminder completed

**Rich Features:**
- `checkItems`: JSON array of checklist items (if type=checklist)
  ```json
  [
    {"id": "string", "text": "string", "checked": boolean}
  ]
  ```
- `labels`: JSON array of label names
  ```json
  ["work", "ideas", "urgent"]
  ```
- `images`: JSON array of image URLs or base64
  ```json
  ["data:image/png;base64,..."]
  ```
- `links`: JSON array of link metadata
- `reminder`: ISO8601 datetime for reminder
- `reminderRecurrence`: Recurrence pattern
- `reminderLocation`: Location-based reminders (future)
- `isMarkdown`: Enable markdown rendering
- `size`: Visual size (small, medium, large)
- `embedding`: Vector embeddings for semantic search (JSON)

**Ordering:**
- `order`: Manual sort order (drag-and-drop)

**Indexes:**
- `isPinned`: Fast lookup for pinned notes
- `isArchived`: Filter archived notes
- `order`: Sort by manual ordering
- `reminder`: Reminder queries (cron jobs)
- `userId`: Filter by user

---

### SystemConfig

Key-value storage for system-wide configuration.

```prisma
model SystemConfig {
  key   String @id
  value String
}
```

**Purpose:** System configuration and feature flags

**Examples:**
- Feature toggles
- System-wide settings
- Admin configuration

---

## Relationships Diagram

```
User (1) ----< (N) Account
  |
  | (1)
  |
  +----< (N) Session
  |
  | (1)
  |
  +----< (N) Note
  |
  | (1)
  |
  +----< (N) Label

VerificationToken (standalone)

SystemConfig (standalone)
```

---

## JSON Field Structures

### checkItems (Note)
Array of checklist items:
```json
[
  {
    "id": "unique-id",
    "text": "Task description",
    "checked": false
  }
]
```

### labels (Note)
Array of label names (strings):
```json
[
  "work",
  "ideas",
  "urgent"
]
```

### images (Note)
Array of image URLs or base64 strings:
```json
[
  "data:image/png;base64,iVBORw0KGgo...",
  "/uploads/images/note-image.png"
]
```

### links (Note)
Array of link metadata (structure TBD):
```json
[
  {
    "url": "https://example.com",
    "title": "Example Site",
    "description": "..."
  }
]
```

### embedding (Note)
Vector embeddings for semantic search (JSON string):
```json
[0.123, -0.456, 0.789, ...]
```

---

## Database Operations

### Common Queries

**Get active user's notes:**
```typescript
const notes = await prisma.note.findMany({
  where: {
    userId: session.user.id,
    isArchived: false
  },
  orderBy: [
    { isPinned: 'desc' },
    { order: 'asc' },
    { updatedAt: 'desc' }
  ]
})
```

**Search notes:**
```typescript
const notes = await prisma.note.findMany({
  where: {
    userId: session.user.id,
    OR: [
      { title: { contains: query, mode: 'insensitive' } },
      { content: { contains: query, mode: 'insensitive' } }
    ]
  }
})
```

**Get user's labels:**
```typescript
const labels = await prisma.label.findMany({
  where: { userId: session.user.id },
  orderBy: { name: 'asc' }
})
```

---

## Data Integrity

**Cascade Deletes:**
- When User is deleted: delete their Accounts, Sessions, Notes, and Labels
- Maintains referential integrity

**Unique Constraints:**
- User.email
- Account.provider + Account.providerAccountId
- VerificationToken.identifier + VerificationToken.token
- Label.name + Label.userId (per user)

**Indexes:**
- All foreign keys indexed
- Frequently queried fields indexed (isPinned, isArchived, order, reminder, userId)

---

## Migration Notes

**Current State:**
- SQLite database for local development
- Better-sqlite3 adapter for embedded usage
- Optional LibSQL adapter for cloud deployment (Turso)

**Schema Evolution:**
- `Label.userId` made optional for migration (logic enforces ownership)
- JSON fields stored as strings (parsed in application layer)
- Future: Consider PostgreSQL for production with proper JSONB support

---

## Database Size Considerations

**SQLite Limits:**
- Max database size: 281 TB (theoretical)
- Max row size: 1 GB
- Max string/blob size: 1 GB

**Practical Considerations:**
- Base64 images in Note.images could bloat database
- Consider storing large files in filesystem and storing paths
- Vector embeddings (Note.embedding) will grow with data

**Recommendations:**
- Use CDN or object storage for images in production
- Implement image compression before storage
- Monitor database size with large note collections
