docs: complete code-reality alignment for remaining docs + reconcile issue set
Remaining docs updated to match code (the docs that the first pass had not covered):
- Flows: Chat, Referral, Rating, Registration, Google OAuth, Negotiation, Payout,
Trezor Safekeeping — corrected endpoints, socket events, status enums, auth gaps
- API Reference: User API, Trezor API — admin route prefix/verb/status corrections,
added undocumented endpoints (ton-proof challenge, profile email verify,
GET /trezor/account, POST /trezor/verify-operation)
- Data Models: Chat, Notification, Payment, PointTransaction, User — corrected
enums (PaymentProvider, escrowState, PointTransaction.type, User.status),
90-day notification TTL, soft-delete semantics, wallet fields
Trezor "zero frontend" finding (audit C31/C32) corrected as STALE:
- Verified current code HAS a full frontend Trezor implementation (admin/trezor
page, TrezorSettingsView, trezorConnector via @trezor/connect-web,
TrezorSignDialog, actions/trezor.ts building the {message,signature} object)
- Fixed Trezor Safekeeping Flow doc (removed false "no frontend" warnings)
- Reclassified ISSUE-012 as invalid/superseded with explanation
Issue set reconciled to a single canonical numbering (ISSUE-001..054):
- Adopted the comprehensive 51-issue set (long-slug, fully indexed)
- Removed 35 superseded short-slug duplicates from the first pass
- Removed a duplicate ISSUE-046 file
- Added 3 issues the 51-set lacked: ISSUE-052 (completed-not-counted-in-stats),
ISSUE-053 (axios 401-only interceptor), ISSUE-054 (rate limiter counts all attempts)
- Regenerated Issues Index: 53 open (14 critical, 39 major) + 1 invalid
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ aliases: [Conversation, IChat, IMessage]
|
||||
---
|
||||
|
||||
# Chat
|
||||
> **Last updated:** 2026-05-29 — aligned with code (see [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md))
|
||||
|
||||
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`).
|
||||
|
||||
@@ -16,6 +17,9 @@ Conversation container with embedded messages. Used for buyer-seller direct chat
|
||||
> [!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.
|
||||
|
||||
> [!warning] `relatedTo` is NOT set via `POST /api/chat`
|
||||
> Although `relatedTo` exists in the schema, it is **not accepted** by the `POST /api/chat` create endpoint. Purchase-request linkage is established server-side through the dedicated `POST /api/chat/purchase-request`, not by passing `relatedTo` to the generic create endpoint.
|
||||
|
||||
## Schema — Chat
|
||||
|
||||
| Field | Type | Required | Default | Validation | Index | Description |
|
||||
@@ -27,10 +31,10 @@ Conversation container with embedded messages. Used for buyer-seller direct chat
|
||||
| `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. |
|
||||
| `participants[].leftAt` | Date | no | — | — | — | Set when the participant is removed (soft removal). |
|
||||
| `participants[].isActive` | Boolean | no | `true` | — | — | Still a participant. Set to `false` on soft removal (subdocument is kept). |
|
||||
| `messages[]` | Subdocument[] | no | `[]` | — | yes (`messages.timestamp`) | Embedded messages. |
|
||||
| `relatedTo.type` | String | no | — | enum: `PurchaseRequest` / `SellerOffer` / `Transaction` | yes (compound) | Linked entity kind. |
|
||||
| `relatedTo.type` | String | no | — | enum: `PurchaseRequest` / `SellerOffer` / `Transaction` | yes (compound) | Linked entity kind. **Not accepted via `POST /api/chat`** — set only via `POST /api/chat/purchase-request`. |
|
||||
| `relatedTo.id` | ObjectId | no | — | — | yes (compound) | Linked entity id. |
|
||||
| `lastMessage.content` | String | no | — | — | — | Snapshot for list views. |
|
||||
| `lastMessage.senderId` | ObjectId → [[User]] | no | — | — | — | Last sender. |
|
||||
@@ -50,13 +54,16 @@ Conversation container with embedded messages. Used for buyer-seller direct chat
|
||||
> [!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.
|
||||
|
||||
> [!note] Soft removal of participants
|
||||
> Removing a participant (via `DELETE /api/chat/:id/participants/:participantId`) does **not** delete the subdocument. It is a soft removal: `isActive` is set to `false` and `leftAt` is timestamped, preserving message attribution and history.
|
||||
|
||||
## 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. |
|
||||
| `content` | String | yes | — | **maxlength 5000** | Message body. Enforced at both schema and controller. |
|
||||
| `messageType` | String | no | `text` | enum: `text` / `image` / `file` / `system` | Body kind. |
|
||||
| `fileUrl` | String | no | — | — | If file/image. |
|
||||
| `fileName` | String | no | — | — | Original filename. |
|
||||
@@ -65,10 +72,14 @@ Conversation container with embedded messages. Used for buyer-seller direct chat
|
||||
| `isRead` | Boolean | no | `false` | — | Read flag. |
|
||||
| `isEdited` | Boolean | no | `false` | — | Edited flag. |
|
||||
| `editedAt` | Date | no | — | — | When edited. |
|
||||
| `deletedAt` | Date | no | — | — | Set on soft-delete; `content` is cleared but the subdocument is kept. |
|
||||
| `replyTo` | ObjectId | no | — | — | Reply target message id. |
|
||||
| `reactions[].userId` | ObjectId → [[User]] | no | — | — | Reacting user. |
|
||||
| `reactions[].reaction` | String | no | — | maxlength 10 | Emoji. |
|
||||
|
||||
> [!note] Messages are soft-deleted
|
||||
> Deleting a message sets `deletedAt` and clears `content` (the body becomes empty). The message subdocument is **not** physically removed from `messages[]`, and a `message-deleted` socket event is emitted.
|
||||
|
||||
## Virtuals
|
||||
|
||||
| Virtual | Returns | Definition |
|
||||
@@ -97,7 +108,7 @@ Defined at `backend/src/models/Chat.ts:243-247`:
|
||||
| --- | --- |
|
||||
| `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` |
|
||||
| `markAsRead(userId, messageIds?: Types.ObjectId[]): void` | Marks listed messages (or all when `messageIds` is empty/omitted) as read for the user, zeros their unread counter, and updates `lastSeen`. `backend/src/models/Chat.ts:308` |
|
||||
|
||||
## Static Methods
|
||||
|
||||
|
||||
Reference in New Issue
Block a user