Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5493ef9
chore(types): update Post types and Document interfaces for migration
shubham-01-star Mar 21, 2026
7cccc5c
feat(models): implement expanded Post model with legacy fields and in…
shubham-01-star Mar 21, 2026
0fc0fcf
test(models): add comprehensive unit tests for Post model
shubham-01-star Mar 21, 2026
676ebb2
fix(models): update User model types import path to use absolute alias
shubham-01-star Mar 21, 2026
c64c6d0
types(comment): add deleted field and align Comment interface with le…
shubham-01-star Mar 21, 2026
afaa6be
feat(comment): expand Comment schema with text index, statics, and de…
shubham-01-star Mar 21, 2026
9541916
test(comment): add unit tests for Comment schema validation and stati…
shubham-01-star Mar 21, 2026
285502c
feat: Introduce Quote Mongoose model with its schema, static methods,…
shubham-01-star Mar 21, 2026
8dbfb77
refactor(types): update shared Mongoose types for remaining dependent…
shubham-01-star Mar 22, 2026
77df5e9
refactor(models): migrate Message and MessageRoom models to TypeScript
shubham-01-star Mar 22, 2026
828c698
refactor(models): migrate Vote models to TypeScript
shubham-01-star Mar 22, 2026
932dab0
refactor(models): migrate Reaction, Roster, and Typing models to Type…
shubham-01-star Mar 22, 2026
ea8392e
refactor(models): migrate UserReport model to TypeScript and update m…
shubham-01-star Mar 22, 2026
48aa9e3
test(config): add schema test helpers and update jest config
shubham-01-star Mar 24, 2026
2c04903
test(models): add schema validation tests for User, Post, Comment, Quote
shubham-01-star Mar 24, 2026
3d4c925
test(models): add schema validation tests for Vote and VoteLog
shubham-01-star Mar 24, 2026
09f76cb
test(models): add schema validation tests for Message and MessageRoom
shubham-01-star Mar 24, 2026
b71f833
test(models): add schema validation tests for Reaction, Roster, Prese…
shubham-01-star Mar 24, 2026
1bcf54d
test(models): add schema validation tests for Activity, Collection, C…
shubham-01-star Mar 24, 2026
ddaf07e
test(models): add schema validation tests for Notification, BotReport…
shubham-01-star Mar 24, 2026
ceda0b3
test(models): add CRUD and schema tests for SolidConnection
shubham-01-star Mar 24, 2026
da4f74a
test(models): fix Comment and Quote CRUD test mock setup
shubham-01-star Mar 24, 2026
b20db27
Merge branch 'QuoteVote:main' into 7.24-test-mongoose-models
shubham-01-star Mar 25, 2026
3f3f3a2
chore(schema): strip extra attributes from Content schema and add tim…
shubham-01-star Mar 25, 2026
54f76b6
chore(schema): refine BotReport, UserInvite, UserReputation fields an…
shubham-01-star Mar 25, 2026
6dd7aaa
chore(schema): align timestamp handling for Group, Notification, Acti…
shubham-01-star Mar 25, 2026
26d4167
test(schema): add CRUD mock suites for simple Prisma models
shubham-01-star Mar 25, 2026
db911ae
chore(schema): clean up Content schema fields and add timestamps
shubham-01-star Mar 25, 2026
fda028e
fix(prisma): align User schema with Mongoose model
shubham-01-star Mar 31, 2026
d254043
fix(prisma): align Post, Comment, Quote, Vote, Reaction schemas
shubham-01-star Mar 31, 2026
c405f89
fix(prisma): add timestamps and compound index to messaging models
shubham-01-star Mar 31, 2026
e906e6f
fix(prisma): add timestamps, expiresAt, and indexes to Presence and T…
shubham-01-star Mar 31, 2026
5659be7
test(prisma): add relationship CRUD tests for all dependent models
shubham-01-star Mar 31, 2026
5da4f8b
feat: implement Prisma schema models and unit tests for user authenti…
shubham-01-star Apr 1, 2026
4c79320
fix(prisma): update stale PostMessage reference in health-check
shubham-01-star Apr 19, 2026
30a4891
feat(prisma): add replica-set preflight guard for Prisma scripts
shubham-01-star Apr 19, 2026
cd6d42c
chore(prisma): rename prisma:studio, add sync/parity scripts, gate te…
shubham-01-star Apr 19, 2026
2fc1db2
feat(prisma): cover all 24 models in prisma-crud-test.ts with deep tr…
shubham-01-star Apr 19, 2026
e265b6d
feat(prisma): add Mongoose-Prisma parity script with functional query…
shubham-01-star Apr 19, 2026
9fa2db1
test(prisma): add missing model suites and 3-level relationship trave…
shubham-01-star Apr 19, 2026
9c05dd0
docs(prisma): document MongoDB migration strategy and replica-set req…
shubham-01-star Apr 19, 2026
8840e42
Merge branch 'main' into 7.27-prisma-migration-sync
shubham-01-star Apr 19, 2026
f1ba2ef
Merge branch 'QuoteVote:main' into 7.27-prisma-migration-sync
shubham-01-star Apr 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions docs/prisma-migration-strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Prisma Migration & Database Sync Strategy

## TL;DR

**Do not run `prisma migrate dev` on this project.** It does not support MongoDB. Use `pnpm prisma:push` (or `pnpm prisma:sync`) instead.

---

## Why Not `prisma migrate dev`?

Prisma's `migrate dev`, `migrate deploy`, and `migrate reset` commands are **SQL-only**. They generate `.sql` files in `prisma/migrations/` that describe schema diffs — which is meaningless for MongoDB since MongoDB is schemaless at the database level.

Running `prisma migrate dev` against a MongoDB datasource fails with:

```
Error: Prisma migrate does not support MongoDB.
```

The ticket text (7.27) references `prisma migrate dev --name init`, but the correct MongoDB equivalent is `prisma db push`.

## The MongoDB Sync Workflow

### Everyday flow (schema change → live DB)

1. Edit schema files in `prisma/schema/*.prisma`
2. Validate syntax:
```bash
pnpm prisma:validate
```
3. Push schema to the live MongoDB (creates/updates indexes, syncs metadata):
```bash
pnpm prisma:push
```
4. Regenerate Prisma Client so TypeScript sees the new types:
```bash
pnpm prisma:generate
```

Or run the combined command:

```bash
pnpm prisma:sync # = prisma:push && prisma:generate
```

### What `db push` actually does

- Syncs the Prisma schema metadata with the connected MongoDB database
- Creates any missing indexes defined in `@@index` / `@@unique`
- **Does NOT** create migration history files — MongoDB is authoritative for data, Prisma only tracks schema shape
- Is **idempotent** — safe to run repeatedly
- Does **NOT** drop data unless `--accept-data-loss` is explicitly passed

## Relationship to Mongoose

Both Mongoose (primary ORM) and Prisma (migration target) read and write the same MongoDB collections. Field-name mismatches between the two ORMs are bridged with `@map()` directives on the Prisma schema (see `docs/prisma-relation-design.md`).

Because MongoDB is schemaless, **old documents written before a new field was added will simply not have that field** — they'll surface as `null` / `undefined` on read. This is acceptable for optional fields and `@default(now())` fields (which only apply on new inserts). For required fields added later, a backfill script is needed (see below).

## Data Transformation / Backfill Pattern

If a future schema change requires transforming existing documents (e.g., renaming a field across all docs, splitting a field, enforcing a new required field), put a one-off script in `scripts/migrations/` following this pattern:

```ts
// scripts/migrations/YYYY-MM-DD-description.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
// 1. Query affected docs
// 2. Transform each doc
// 3. updateMany / $currentDate / etc
// 4. Log count of transformed docs
}

main()
.then(() => process.exit(0))
.catch((err) => {
console.error(err);
process.exit(1);
})
.finally(() => prisma.$disconnect());
```

Rules:

- **Idempotent** — safe to re-run. Use `$set` + a marker field, or check for the new shape before transforming.
- **Logged** — print before/after counts so an operator can verify.
- **Guarded** — use `if (process.env.NODE_ENV !== 'production') { ... }` guards where destructive.
- **Committed** — keep migration scripts in version control forever, even after they've run, as a record of past changes.

No backfill scripts exist yet because all new fields landed in 7.25–7.26 are either optional, `@default(now())`, or arrays with sensible defaults.

## Parity Verification: `pnpm prisma:parity`

Because Mongoose and Prisma coexist, the risk is that field-name mappings drift silently — for example, renaming a Mongoose field without updating the corresponding Prisma `@map()`.

`scripts/prisma-mongoose-parity.ts` (wired as `pnpm prisma:parity`) detects drift at runtime:

1. Connects both Mongoose and Prisma to the same DB
2. For each critical model (User, Post, MessageRoom), runs two round-trips:
- **Mongoose → Prisma**: create doc via Mongoose, read via Prisma, assert all mapped fields (`admin`/`isAdmin`, `_followingId`/`followingIds`, `enable_voting`/`enableVoting`, `users`/`userIds`) surface correctly on the Prisma side
- **Prisma → Mongoose**: create via Prisma, read raw via Mongoose, assert the MongoDB document uses the expected field names
3. Runs a **functional parity** round — seeds a `User`+`Group`+`Post` via Mongoose, then:
- Reads the post via `Post.findById(...).populate('userId', 'username')` (Mongoose)
- Reads the same post via `prisma.post.findUnique({ include: { user: { select: ... } } })` (Prisma)
- Asserts the post title, populated author id, and author username match between the two ORMs
- Asserts `Post.countDocuments({ userId })` equals `prisma.post.count({ where: { userId } })`
4. Cleans up all docs it created (LIFO, in a `finally` block)
5. Exits `0` on success, `1` on any mismatch (with a diff report)

Run it whenever schema/mapping changes:

```bash
pnpm prisma:parity
```

**Requires a live MongoDB instance running as a replica set** (local or staging). Not run in CI for now.

### MongoDB replica set requirement

Prisma's MongoDB connector uses transactions internally for `create`/`update`/`delete`, and MongoDB only supports transactions on replica sets. A standalone `mongod` will fail with:

```
Prisma needs to perform transactions, which requires your MongoDB server to be run as a replica set.
```

**Fix — pick one:**

1. **Local single-node replica set:** start `mongod --replSet rs0`, then once in `mongosh`: `rs.initiate()`. Update `DATABASE_URL` to include `?replicaSet=rs0&directConnection=true`.
2. **Docker:** `docker run -d --name mongo-rs -p 27017:27017 mongo:7 --replSet rs0 --bind_ip_all` then `docker exec -it mongo-rs mongosh --eval "rs.initiate()"`.
3. **MongoDB Atlas:** Atlas clusters are already replica sets — just paste the connection string into `DATABASE_URL`.

Mongoose operations work against a standalone because Mongoose issues plain `insertOne`/`updateOne` without wrapping them in a transaction — so this constraint only surfaces once Prisma is introduced.

## Integrity Checks

Beyond parity, these commands verify the DB state:

| Command | What it checks |
|---|---|
| `pnpm prisma:validate` | Schema syntax is valid (static check, no DB needed) |
| `pnpm prisma:health` | DB connection works, all 24 models are visible |
| `pnpm prisma:test` | CRUD + relationship round-trip for **all 24 models** (User, Group, Post, Comment, Quote, Vote, VoteLog, Reaction, Message, DirectMessage, MessageRoom, Notification, Activity, Roster, Presence, Typing, UserInvite, UserReport, BotReport, UserReputation, Domain, Creator, Content, Collection) plus 3-level deep traversals |
| `pnpm test:prisma` | Full Jest integration suite — every model, unique-constraint enforcement, 3-level deep `include` traversal (user → posts → comments → commenter; room → messages → reactions → reactor), and a delegate smoke test that calls `count()` on all 24 models |
| `pnpm prisma:parity` | Mongoose↔Prisma field-mapping parity **plus functional parity** — the same logical query via Mongoose `populate()` and Prisma `include()` must return equivalent author id/username and document counts |

## When to Re-Sync

Re-run `pnpm prisma:sync` after:

- Adding or removing a model
- Adding/removing/renaming a field on any model
- Changing an index (`@@index`, `@@unique`)
- Changing an enum
- Bumping `@prisma/client` or `prisma` major versions

Skip the push if only:

- Editing comments
- Reformatting (`pnpm prisma:format`)
- Adding relation fields that don't change the underlying indexes
50 changes: 50 additions & 0 deletions docs/prisma-relation-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Prisma Relation Design — MongoDB

## Overview

QuoteVote uses MongoDB with both Mongoose (primary) and Prisma (migration target) reading from the same collections. This document covers key design decisions for Prisma schema alignment.

## Logical References, Not Foreign Keys

MongoDB does not enforce foreign key constraints at the database level. All relations in Prisma are **logical references** managed at the application layer. This means:

- Prisma `@relation` attributes define how the client resolves `include` queries
- No cascading deletes happen automatically — cleanup is application responsibility
- Orphaned references are possible if documents are deleted without updating referrers

## Field Name Mapping (`@map`)

Since Mongoose and Prisma share the same MongoDB collections, Prisma field names must match the actual MongoDB document field names. Where Prisma uses camelCase but MongoDB stores snake_case or prefixed names, `@map()` bridges the gap:

| Prisma Field | MongoDB Field | Mapping |
|---|---|---|
| `User.isAdmin` | `admin` | `@map("admin")` |
| `User.followingIds` | `_followingId` | `@map("_followingId")` |
| `User.followerIds` | `_followersId` | `@map("_followersId")` |
| `Post.enableVoting` | `enable_voting` | `@map("enable_voting")` |
| `MessageRoom.userIds` | `users` | `@map("users")` |
| `Message.mutationType` | `mutation_type` | `@map("mutation_type")` |

## Model Naming

The Prisma model `Message` maps to the `messages` collection via `@@map("messages")`, matching the Mongoose model name `Message`. The legacy `DirectMessage` model maps to `directmessages` for the old `Messages.js` simple messages.

## Embedded vs Referenced Documents

- **User.reputation**: Mongoose embeds reputation data directly on the User document. Prisma uses an `EmbeddedReputation` composite type to match this.
- **UserReputation**: A separate standalone model also exists (from `UserReputation.ts`), used for detailed reputation tracking with history.
- **Message.readByDetailed / deliveredTo**: Mongoose uses embedded subdocuments. Prisma uses composite types `ReadByDetail` and `DeliveredToDetail`.

## Array Type Decisions

Some Mongoose arrays store plain strings (not ObjectIds):

- `Post.bookmarkedBy`, `Post.rejectedBy`, `Post.approvedBy`, `Post.reportedBy`, `Post.votedBy` — all `String[]` in Prisma (no `@db.ObjectId`)

Arrays that store ObjectId references use `@db.ObjectId`:

- `User.followingIds`, `User.followerIds`, `User.blockedUserIds`, `MessageRoom.userIds`, `Message.readBy`

## Many-to-Many via Arrays

MongoDB handles many-to-many relationships using ObjectId arrays rather than junction tables. For example, `MessageRoom.userIds` stores an array of User ObjectIds. Prisma models this as `String[] @db.ObjectId` since MongoDB Prisma does not support implicit many-to-many relations.
Loading
Loading