Initial commit: nick docs

This commit is contained in:
moojttaba
2026-05-23 20:35:34 +03:30
commit 0da235ae27
90 changed files with 18268 additions and 0 deletions

144
02 - Data Models/Chat.md Normal file
View File

@@ -0,0 +1,144 @@
---
title: Chat
tags: [data-model, mongoose]
aliases: [Conversation, IChat, IMessage]
---
# Chat
Conversation container with embedded messages. Used for buyer-seller direct chats, group chats, and support tickets. Each chat carries a list of participants, an embedded `messages[]` array (with reactions, replies, edit history), a denormalised `lastMessage` snapshot for list views, and per-user `unreadCounts`. A chat can be linked to any other entity through the `relatedTo` discriminator (currently `PurchaseRequest`, `SellerOffer`, or `Transaction`).
> [!note] Source
> `backend/src/models/Chat.ts:130` — chat schema definition
> `backend/src/models/Chat.ts:69` — message subdocument schema
> `backend/src/models/Chat.ts:348` — model export
> [!warning] Embedded messages
> Messages live inside the chat document. Very long-running chats can grow past the 16 MB document limit. Treat this as a known constraint of the current schema.
## Schema — Chat
| Field | Type | Required | Default | Validation | Index | Description |
| --- | --- | --- | --- | --- | --- | --- |
| `type` | String | yes | `direct` | enum: `direct` / `group` / `support` | yes | Conversation type. |
| `name` | String | no | — | maxlength 100 | — | Display name (group chats). |
| `description` | String | no | — | maxlength 500 | — | Optional description. |
| `participants[].userId` | ObjectId → [[User]] | yes | — | — | yes | Member id. |
| `participants[].role` | String | no | `member` | enum: `member` / `admin` / `owner` | — | Member role. |
| `participants[].joinedAt` | Date | no | `Date.now` | — | — | Join time. |
| `participants[].lastSeen` | Date | no | — | — | — | Last activity. |
| `participants[].leftAt` | Date | no | — | — | — | If left, when. |
| `participants[].isActive` | Boolean | no | `true` | — | — | Still a participant. |
| `messages[]` | Subdocument[] | no | `[]` | — | yes (`messages.timestamp`) | Embedded messages. |
| `relatedTo.type` | String | no | — | enum: `PurchaseRequest` / `SellerOffer` / `Transaction` | yes (compound) | Linked entity kind. |
| `relatedTo.id` | ObjectId | no | — | — | yes (compound) | Linked entity id. |
| `lastMessage.content` | String | no | — | — | — | Snapshot for list views. |
| `lastMessage.senderId` | ObjectId → [[User]] | no | — | — | — | Last sender. |
| `lastMessage.timestamp` | Date | no | — | — | — | Last message time. |
| `lastMessage.messageType` | String | no | — | — | — | Last message type. |
| `unreadCounts[].userId` | ObjectId → [[User]] | no | — | — | — | User the counter belongs to. |
| `unreadCounts[].count` | Number | no | `0` | — | — | Number of unread messages. |
| `settings.isArchived` | Boolean | no | `false` | — | — | Archived flag. |
| `settings.isMuted` | Boolean | no | `false` | — | — | Muted flag. |
| `settings.mutedUntil` | Date | no | — | — | — | Mute expiry. |
| `settings.notifications` | Boolean | no | `true` | — | — | Per-chat notification toggle. |
| `metadata.createdBy` | ObjectId → [[User]] | yes | — | — | — | Original creator. |
| `metadata.createdAt` | Date | no | `Date.now` | — | — | Created timestamp. |
| `metadata.updatedAt` | Date | no | `Date.now` | — | — | Touched by pre-save. |
| `metadata.lastActivity` | Date | no | `Date.now` | — | yes (desc) | Sort key for chat lists. |
> [!note] No top-level `timestamps`
> Unlike most models, this schema does not pass `{ timestamps: true }`. It uses its own `metadata.createdAt` / `metadata.updatedAt` instead, maintained by the pre-save hook.
## Schema — Message (embedded)
| Field | Type | Required | Default | Validation | Description |
| --- | --- | --- | --- | --- | --- |
| `senderId` | ObjectId → [[User]] | yes | — | — | Author. |
| `senderType` | String | no | `User` | — | Currently fixed. |
| `content` | String | yes | — | maxlength 5000 | Message body. |
| `messageType` | String | no | `text` | enum: `text` / `image` / `file` / `system` | Body kind. |
| `fileUrl` | String | no | — | — | If file/image. |
| `fileName` | String | no | — | — | Original filename. |
| `fileSize` | Number | no | — | — | Bytes. |
| `timestamp` | Date | no | `Date.now` | — | Sent time. |
| `isRead` | Boolean | no | `false` | — | Read flag. |
| `isEdited` | Boolean | no | `false` | — | Edited flag. |
| `editedAt` | Date | no | — | — | When edited. |
| `replyTo` | ObjectId | no | — | — | Reply target message id. |
| `reactions[].userId` | ObjectId → [[User]] | no | — | — | Reacting user. |
| `reactions[].reaction` | String | no | — | maxlength 10 | Emoji. |
## Virtuals
| Virtual | Returns | Definition |
| --- | --- | --- |
| `participantsCount` | Count of active participants | `backend/src/models/Chat.ts:259` |
## Indexes
Defined at `backend/src/models/Chat.ts:243-247`:
- `{ 'participants.userId': 1 }`
- `{ 'metadata.lastActivity': -1 }`
- `{ 'relatedTo.type': 1, 'relatedTo.id': 1 }`
- `{ 'messages.timestamp': -1 }`
- `{ type: 1 }`
## Pre/Post Hooks
| Hook | Behaviour |
| --- | --- |
| `pre('save')` (`backend/src/models/Chat.ts:250`) | Updates `metadata.updatedAt` and refreshes `metadata.lastActivity` when there are messages. |
## Instance Methods
| Signature | Purpose |
| --- | --- |
| `getUnreadCount(userId: Types.ObjectId): number` | Returns the unread counter for a participant. `backend/src/models/Chat.ts:264` |
| `addMessage(messageData: Partial<IMessage>): IMessage` | Pushes a message, updates `lastMessage`, increments unread counters for everyone except the sender, and bumps `lastActivity`. `backend/src/models/Chat.ts:270` |
| `markAsRead(userId, messageIds?: Types.ObjectId[]): void` | Marks listed messages (or all) as read for the user, zeros their unread counter, and updates `lastSeen`. `backend/src/models/Chat.ts:308` |
## Static Methods
None defined.
## Relationships
- **References**: [[User]] (`participants[].userId`, `messages[].senderId`, `messages[].reactions[].userId`, `lastMessage.senderId`, `unreadCounts[].userId`, `metadata.createdBy`).
- **Referenced by**: [[Dispute]] (`chatId`), and any [[PurchaseRequest]] / [[SellerOffer]] indirectly through `relatedTo`.
## State Transitions
No top-level status. Chat-level archival is a boolean flag (`settings.isArchived`):
```mermaid
stateDiagram-v2
[*] --> active
active --> muted : user mutes
muted --> active : unmute / mute expires
active --> archived : user archives
archived --> active : restore
```
## Common Queries
```ts
// A user's recent chats
Chat.find({ 'participants.userId': userId, 'participants.isActive': true })
.sort({ 'metadata.lastActivity': -1 });
// Chat for a purchase request
Chat.findOne({ 'relatedTo.type': 'PurchaseRequest', 'relatedTo.id': prId });
// Append a message
const chat = await Chat.findById(id);
chat.addMessage({ senderId, content: 'hi', messageType: 'text' });
await chat.save();
// Mark read
chat.markAsRead(userId);
await chat.save();
```
Related: [[User]], [[PurchaseRequest]], [[SellerOffer]], [[Dispute]].