@clinchdate/api-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +711 -0
- package/dist/client.d.ts +30 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +196 -0
- package/dist/client.js.map +1 -0
- package/dist/config/environments.d.ts +24 -0
- package/dist/config/environments.d.ts.map +1 -0
- package/dist/config/environments.js +24 -0
- package/dist/config/environments.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/hooks/useAuth.d.ts +13 -0
- package/dist/hooks/useAuth.d.ts.map +1 -0
- package/dist/hooks/useAuth.js +66 -0
- package/dist/hooks/useAuth.js.map +1 -0
- package/dist/hooks/useCall.d.ts +55 -0
- package/dist/hooks/useCall.d.ts.map +1 -0
- package/dist/hooks/useCall.js +148 -0
- package/dist/hooks/useCall.js.map +1 -0
- package/dist/hooks/useChat.d.ts +13 -0
- package/dist/hooks/useChat.d.ts.map +1 -0
- package/dist/hooks/useChat.js +76 -0
- package/dist/hooks/useChat.js.map +1 -0
- package/dist/hooks/useMatch.d.ts +15 -0
- package/dist/hooks/useMatch.d.ts.map +1 -0
- package/dist/hooks/useMatch.js +72 -0
- package/dist/hooks/useMatch.js.map +1 -0
- package/dist/hooks/useUser.d.ts +22 -0
- package/dist/hooks/useUser.d.ts.map +1 -0
- package/dist/hooks/useUser.js +170 -0
- package/dist/hooks/useUser.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/auth.interceptor.d.ts +14 -0
- package/dist/interceptors/auth.interceptor.d.ts.map +1 -0
- package/dist/interceptors/auth.interceptor.js +61 -0
- package/dist/interceptors/auth.interceptor.js.map +1 -0
- package/dist/interceptors/error.interceptor.d.ts +12 -0
- package/dist/interceptors/error.interceptor.d.ts.map +1 -0
- package/dist/interceptors/error.interceptor.js +50 -0
- package/dist/interceptors/error.interceptor.js.map +1 -0
- package/dist/interceptors/response.interceptor.d.ts +2 -0
- package/dist/interceptors/response.interceptor.d.ts.map +1 -0
- package/dist/interceptors/response.interceptor.js +2 -0
- package/dist/interceptors/response.interceptor.js.map +1 -0
- package/dist/services/auth.service.d.ts +12 -0
- package/dist/services/auth.service.d.ts.map +1 -0
- package/dist/services/auth.service.js +37 -0
- package/dist/services/auth.service.js.map +1 -0
- package/dist/services/base.service.d.ts +10 -0
- package/dist/services/base.service.d.ts.map +1 -0
- package/dist/services/base.service.js +22 -0
- package/dist/services/base.service.js.map +1 -0
- package/dist/services/call.service.d.ts +80 -0
- package/dist/services/call.service.d.ts.map +1 -0
- package/dist/services/call.service.js +57 -0
- package/dist/services/call.service.js.map +1 -0
- package/dist/services/chat.service.d.ts +16 -0
- package/dist/services/chat.service.d.ts.map +1 -0
- package/dist/services/chat.service.js +33 -0
- package/dist/services/chat.service.js.map +1 -0
- package/dist/services/index.d.ts +23 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +23 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/match.service.d.ts +14 -0
- package/dist/services/match.service.d.ts.map +1 -0
- package/dist/services/match.service.js +23 -0
- package/dist/services/match.service.js.map +1 -0
- package/dist/services/notification.service.d.ts +16 -0
- package/dist/services/notification.service.d.ts.map +1 -0
- package/dist/services/notification.service.js +31 -0
- package/dist/services/notification.service.js.map +1 -0
- package/dist/services/payment.service.d.ts +15 -0
- package/dist/services/payment.service.d.ts.map +1 -0
- package/dist/services/payment.service.js +32 -0
- package/dist/services/payment.service.js.map +1 -0
- package/dist/services/user.service.d.ts +16 -0
- package/dist/services/user.service.d.ts.map +1 -0
- package/dist/services/user.service.js +34 -0
- package/dist/services/user.service.js.map +1 -0
- package/dist/types/auth.types.d.ts +49 -0
- package/dist/types/auth.types.d.ts.map +1 -0
- package/dist/types/auth.types.js +2 -0
- package/dist/types/auth.types.js.map +1 -0
- package/dist/types/chat.types.d.ts +35 -0
- package/dist/types/chat.types.d.ts.map +1 -0
- package/dist/types/chat.types.js +2 -0
- package/dist/types/chat.types.js.map +1 -0
- package/dist/types/common.types.d.ts +39 -0
- package/dist/types/common.types.d.ts.map +1 -0
- package/dist/types/common.types.js +25 -0
- package/dist/types/common.types.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/match.types.d.ts +40 -0
- package/dist/types/match.types.d.ts.map +1 -0
- package/dist/types/match.types.js +2 -0
- package/dist/types/match.types.js.map +1 -0
- package/dist/types/notification.types.d.ts +26 -0
- package/dist/types/notification.types.d.ts.map +1 -0
- package/dist/types/notification.types.js +2 -0
- package/dist/types/notification.types.js.map +1 -0
- package/dist/types/payment.types.d.ts +47 -0
- package/dist/types/payment.types.d.ts.map +1 -0
- package/dist/types/payment.types.js +2 -0
- package/dist/types/payment.types.js.map +1 -0
- package/dist/types/user.types.d.ts +50 -0
- package/dist/types/user.types.d.ts.map +1 -0
- package/dist/types/user.types.js +2 -0
- package/dist/types/user.types.js.map +1 -0
- package/package.json +120 -0
package/README.MD
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
# ClinchDate — Complete Technical Documentation
|
|
2
|
+
|
|
3
|
+
> Multi-platform dating app targeting Cameroon & West Africa & the World.
|
|
4
|
+
> Backend API · Web App · Mobile App · Admin Panel · Shared API Client SDK
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
1. [Architecture Overview](#architecture-overview)
|
|
11
|
+
2. [Repository Structure](#repository-structure)
|
|
12
|
+
3. [Backend API](#backend-api)
|
|
13
|
+
4. [API Client SDK](#api-client-sdk)
|
|
14
|
+
5. [Twilio Calling System](#twilio-calling-system)
|
|
15
|
+
6. [Database Models](#database-models)
|
|
16
|
+
7. [API Routes Reference](#api-routes-reference)
|
|
17
|
+
8. [Environment Setup](#environment-setup)
|
|
18
|
+
9. [Deployment](#deployment)
|
|
19
|
+
10. [Work Completed](#work-completed)
|
|
20
|
+
11. [Remaining Work](#remaining-work)
|
|
21
|
+
12. [Known Issues & Audit Findings](#known-issues--audit-findings)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Architecture Overview
|
|
26
|
+
|
|
27
|
+
ClinchDate uses a **polyrepo architecture** with five independent codebases sharing a single API client SDK.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
31
|
+
│ Mobile App │ │ Web App │ │ Admin Panel │
|
|
32
|
+
│ React Native │ │ React │ │ React │
|
|
33
|
+
│ Expo Router │ │ Vite │ │ Vite │
|
|
34
|
+
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
|
|
35
|
+
│ │ │
|
|
36
|
+
└──────────┬───────┴───────────────────┘
|
|
37
|
+
│
|
|
38
|
+
┌────────▼────────┐
|
|
39
|
+
│ @clinchdate/ │
|
|
40
|
+
│ api-client SDK │ ← Mandatory for all frontends
|
|
41
|
+
│ (npm package) │
|
|
42
|
+
└────────┬────────┘
|
|
43
|
+
│
|
|
44
|
+
┌────────▼────────┐
|
|
45
|
+
│ Backend API │
|
|
46
|
+
│ Node/Express │
|
|
47
|
+
│ MongoDB Atlas │
|
|
48
|
+
└────────┬────────┘
|
|
49
|
+
│
|
|
50
|
+
┌─────────────┼──────────────┐
|
|
51
|
+
│ │ │
|
|
52
|
+
┌───▼──┐ ┌─────▼────┐ ┌────▼────┐
|
|
53
|
+
│Twilio│ │ Stripe │ │Firebase │
|
|
54
|
+
│Video │ │ Payments │ │ Auth │
|
|
55
|
+
│Voice │ │ │ │ Push │
|
|
56
|
+
└──────┘ └──────────┘ └─────────┘
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Tech Stack:**
|
|
60
|
+
- **Backend:** Node.js, Express, TypeScript, MongoDB (Mongoose), Socket.IO
|
|
61
|
+
- **Mobile:** React Native, Expo Router, TypeScript
|
|
62
|
+
- **Web:** React, Vite, TypeScript
|
|
63
|
+
- **Admin:** React, Vite, TypeScript
|
|
64
|
+
- **SDK:** TypeScript, Axios
|
|
65
|
+
- **Auth:** Firebase Authentication + JWT refresh tokens
|
|
66
|
+
- **Payments:** Stripe (subscriptions, one-time purchases)
|
|
67
|
+
- **Calling:** Twilio Video (peer-to-peer) + Twilio Voice (Client SDK)
|
|
68
|
+
- **Storage:** AWS S3 + CloudFront CDN
|
|
69
|
+
- **Email:** SendGrid dynamic templates
|
|
70
|
+
- **SMS/Verify:** Twilio Verify + Messaging Services
|
|
71
|
+
- **Images:** Cloudinary
|
|
72
|
+
- **Maps:** Google Maps + Places API
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Repository Structure
|
|
77
|
+
|
|
78
|
+
### Backend — `clinchdate-backend/`
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
src/
|
|
82
|
+
├── config/
|
|
83
|
+
│ ├── database.config.ts # MongoDB connection
|
|
84
|
+
│ ├── firebase.config.ts # Firebase Admin SDK
|
|
85
|
+
│ ├── stripe.config.ts # Stripe client
|
|
86
|
+
│ └── twilio.config.ts # ★ Twilio config (NEW)
|
|
87
|
+
│
|
|
88
|
+
├── models/
|
|
89
|
+
│ ├── index.ts # Barrel exports all models
|
|
90
|
+
│ ├── User.model.ts # ✅ Indexes fixed
|
|
91
|
+
│ ├── Admin.model.ts # ✅ Indexes fixed
|
|
92
|
+
│ ├── Profile.model.ts # ✅ Indexes fixed
|
|
93
|
+
│ ├── Match.model.ts # ✅ Indexes fixed
|
|
94
|
+
│ ├── Swipe.model.ts # ✅ Indexes fixed
|
|
95
|
+
│ ├── Conversation.model.ts # ✅ Indexes fixed
|
|
96
|
+
│ ├── Message.model.ts # ✅ Indexes fixed
|
|
97
|
+
│ ├── Chat.model.ts # ✅ Indexes fixed (⚠️ duplicates Message — see audit)
|
|
98
|
+
│ ├── Payment.model.ts # ✅ Indexes fixed
|
|
99
|
+
│ ├── Subscription.model.ts # ✅ Indexes fixed
|
|
100
|
+
│ ├── Report.model.ts # ✅ Indexes fixed
|
|
101
|
+
│ ├── SupportTicket.model.ts # ✅ Indexes fixed
|
|
102
|
+
│ ├── Announcement.model.ts # ✅ Indexes fixed
|
|
103
|
+
│ ├── BlockedUser.model.ts # ✅ Indexes fixed
|
|
104
|
+
│ ├── Boost.model.ts # ✅ Indexes fixed
|
|
105
|
+
│ ├── Event.model.ts # ✅ Indexes fixed
|
|
106
|
+
│ ├── Notification.model.ts # ✅ Indexes fixed
|
|
107
|
+
│ ├── Verification.model.ts # ✅ Indexes fixed
|
|
108
|
+
│ ├── Withdrawal.model.ts # ✅ Indexes fixed (⚠️ missing userId — see audit)
|
|
109
|
+
│ ├── Settings.model.ts # ✅ Already clean
|
|
110
|
+
│ └── Call.model.ts # ★ NEW — Twilio call records
|
|
111
|
+
│
|
|
112
|
+
├── services/
|
|
113
|
+
│ ├── twilio.service.ts # ★ NEW — Twilio SDK wrapper
|
|
114
|
+
│ └── call.service.ts # ★ NEW — Call business logic
|
|
115
|
+
│
|
|
116
|
+
├── controllers/
|
|
117
|
+
│ ├── call.controller.ts # ★ NEW — Call API handlers
|
|
118
|
+
│ └── twilio-webhook.controller.ts # ★ NEW — Webhook handlers
|
|
119
|
+
│
|
|
120
|
+
├── middleware/
|
|
121
|
+
│ ├── auth.middleware.ts # Firebase JWT validation
|
|
122
|
+
│ └── twilio-webhook.middleware.ts # ★ NEW — Signature validation
|
|
123
|
+
│
|
|
124
|
+
├── routes/
|
|
125
|
+
│ ├── call.routes.ts # ★ NEW — /api/v1/calls/*
|
|
126
|
+
│ └── webhook.routes.ts # ★ NEW — /api/v1/webhooks/twilio/*
|
|
127
|
+
│
|
|
128
|
+
├── setup/
|
|
129
|
+
│ └── call.setup.ts # ★ NEW — Bootstrap wiring
|
|
130
|
+
│
|
|
131
|
+
├── utils/
|
|
132
|
+
│ └── logger.ts # Winston/Pino logger
|
|
133
|
+
│
|
|
134
|
+
├── constants/
|
|
135
|
+
│ ├── status.constant.ts # Enum definitions
|
|
136
|
+
│ └── notification.constant.ts # Notification types
|
|
137
|
+
│
|
|
138
|
+
└── server.ts # Express entry point
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### API Client SDK — `clinchdate-client/`
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
src/
|
|
145
|
+
├── config.ts # ★ NEW — SDK configuration singleton
|
|
146
|
+
├── client.ts # ★ NEW — HTTP client (axios + interceptors)
|
|
147
|
+
│
|
|
148
|
+
├── services/
|
|
149
|
+
│ ├── auth.service.ts # Login, register, refresh, logout
|
|
150
|
+
│ ├── user.service.ts # User CRUD, search, preferences
|
|
151
|
+
│ ├── profile.service.ts # Profile management, photos
|
|
152
|
+
│ ├── match.service.ts # Matching, swiping, super likes
|
|
153
|
+
│ ├── chat.service.ts # Conversations, messages, read receipts
|
|
154
|
+
│ ├── call.service.ts # ✅ FIXED — Video & audio calls
|
|
155
|
+
│ ├── payment.service.ts # Stripe payments, subscriptions
|
|
156
|
+
│ ├── event.service.ts # Events CRUD, RSVP
|
|
157
|
+
│ ├── notification.service.ts # Push notifications, preferences
|
|
158
|
+
│ ├── report.service.ts # User reports, blocking
|
|
159
|
+
│ ├── support.service.ts # Support tickets
|
|
160
|
+
│ ├── admin.service.ts # Admin dashboard operations
|
|
161
|
+
│ ├── settings.service.ts # User settings
|
|
162
|
+
│ └── upload.service.ts # File uploads (S3/Cloudinary)
|
|
163
|
+
│
|
|
164
|
+
├── types/ # Shared TypeScript interfaces
|
|
165
|
+
│ ├── user.types.ts
|
|
166
|
+
│ ├── match.types.ts
|
|
167
|
+
│ ├── chat.types.ts
|
|
168
|
+
│ └── ...
|
|
169
|
+
│
|
|
170
|
+
└── index.ts # Barrel export
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Backend API
|
|
176
|
+
|
|
177
|
+
### Quick Start
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# 1. Clone and install
|
|
181
|
+
cd clinchdate-backend
|
|
182
|
+
npm install
|
|
183
|
+
|
|
184
|
+
# 2. Environment setup
|
|
185
|
+
cp .env.example .env
|
|
186
|
+
# Edit .env with your actual credentials (see Environment Setup section)
|
|
187
|
+
|
|
188
|
+
# 3. Install Twilio dependency (NEW)
|
|
189
|
+
npm install twilio
|
|
190
|
+
|
|
191
|
+
# 4. Wire up calling system — add to server.ts:
|
|
192
|
+
# import { setupCallSystem } from './setup/call.setup';
|
|
193
|
+
# setupCallSystem(app); // after your existing route setup
|
|
194
|
+
|
|
195
|
+
# 5. Run
|
|
196
|
+
npm run dev
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Server Startup Checklist
|
|
200
|
+
|
|
201
|
+
When the server starts, you should see:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
✅ MongoDB connected
|
|
205
|
+
✅ Firebase Admin initialized
|
|
206
|
+
✅ Twilio config loaded and validated
|
|
207
|
+
✅ Call routes mounted: /api/v1/calls
|
|
208
|
+
✅ Twilio webhook routes mounted: /api/v1/webhooks/twilio
|
|
209
|
+
✅ Stale call cleanup job started (every 2 min)
|
|
210
|
+
✅ Socket.IO ready
|
|
211
|
+
🚀 Server running on port 3000
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
If you see **zero** Mongoose duplicate index warnings, the model fixes are working correctly.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## API Client SDK
|
|
219
|
+
|
|
220
|
+
The SDK is the **mandatory** interface between all frontends and the backend. No frontend should make direct HTTP calls — everything goes through the SDK.
|
|
221
|
+
|
|
222
|
+
### Installation
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# From the SDK repo
|
|
226
|
+
npm install
|
|
227
|
+
|
|
228
|
+
# Or link locally during development
|
|
229
|
+
npm link
|
|
230
|
+
cd ../clinchdate-web && npm link @clinchdate/api-client
|
|
231
|
+
cd ../clinchdate-mobile && npm link @clinchdate/api-client
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Initialization
|
|
235
|
+
|
|
236
|
+
The SDK must be configured **once** at app startup before any API calls.
|
|
237
|
+
|
|
238
|
+
#### React Native (Mobile)
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// App.tsx or app/_layout.tsx
|
|
242
|
+
import { configure } from '@clinchdate/api-client';
|
|
243
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
244
|
+
|
|
245
|
+
configure({
|
|
246
|
+
baseURL: 'https://api.clinchdate.com/api/v1',
|
|
247
|
+
platform: 'ios', // or 'android'
|
|
248
|
+
getToken: () => AsyncStorage.getItem('accessToken'),
|
|
249
|
+
getRefreshToken: () => AsyncStorage.getItem('refreshToken'),
|
|
250
|
+
onTokenRefreshed: async (tokens) => {
|
|
251
|
+
await AsyncStorage.setItem('accessToken', tokens.accessToken);
|
|
252
|
+
await AsyncStorage.setItem('refreshToken', tokens.refreshToken);
|
|
253
|
+
},
|
|
254
|
+
onAuthFailure: () => {
|
|
255
|
+
// Navigate to login screen
|
|
256
|
+
router.replace('/login');
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### React (Web / Admin)
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// main.tsx or App.tsx
|
|
265
|
+
import { configure } from '@clinchdate/api-client';
|
|
266
|
+
|
|
267
|
+
configure({
|
|
268
|
+
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api/v1',
|
|
269
|
+
platform: 'web', // or 'admin'
|
|
270
|
+
getToken: async () => localStorage.getItem('accessToken'),
|
|
271
|
+
getRefreshToken: async () => localStorage.getItem('refreshToken'),
|
|
272
|
+
onTokenRefreshed: async (tokens) => {
|
|
273
|
+
localStorage.setItem('accessToken', tokens.accessToken);
|
|
274
|
+
localStorage.setItem('refreshToken', tokens.refreshToken);
|
|
275
|
+
},
|
|
276
|
+
onAuthFailure: () => {
|
|
277
|
+
window.location.href = '/login';
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### SDK Features
|
|
283
|
+
|
|
284
|
+
The `client.ts` HTTP client provides:
|
|
285
|
+
|
|
286
|
+
- **Automatic token attachment** — every request gets `Authorization: Bearer <token>`
|
|
287
|
+
- **Silent token refresh** — 401 responses trigger automatic refresh with queued retries
|
|
288
|
+
- **Request queuing** — concurrent requests during refresh are queued, not duplicated
|
|
289
|
+
- **Retry logic** — 5xx and timeout errors auto-retry with exponential backoff (1s, 2s, 4s)
|
|
290
|
+
- **Response envelope unwrapping** — `{ success: true, data: T }` → returns `T` directly
|
|
291
|
+
- **File uploads** — `httpClient.upload()` with progress callback
|
|
292
|
+
- **Error normalization** — all errors become `ApiError` with `statusCode`, `message`, `isNetworkError`, `isAuthError`
|
|
293
|
+
- **Debug mode** — set `debug: true` to log all requests/responses
|
|
294
|
+
- **Platform header** — `X-Platform: ios|android|web|admin` for analytics
|
|
295
|
+
|
|
296
|
+
### Using Services
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { authService } from '@clinchdate/api-client';
|
|
300
|
+
import { matchService } from '@clinchdate/api-client';
|
|
301
|
+
import { callService } from '@clinchdate/api-client';
|
|
302
|
+
|
|
303
|
+
// Auth
|
|
304
|
+
const user = await authService.login({ email, password });
|
|
305
|
+
const tokens = await authService.refreshToken(refreshToken);
|
|
306
|
+
|
|
307
|
+
// Matching
|
|
308
|
+
const matches = await matchService.getMatches();
|
|
309
|
+
await matchService.swipeRight(userId);
|
|
310
|
+
|
|
311
|
+
// Calling
|
|
312
|
+
const { callId, token, roomName } = await callService.initiateVideoCall(receiverId, matchId);
|
|
313
|
+
const voiceToken = await callService.getAudioToken();
|
|
314
|
+
const history = await callService.getCallHistory(20);
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Error Handling
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
import { ApiError } from '@clinchdate/api-client';
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
await matchService.swipeRight(userId);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (error instanceof ApiError) {
|
|
326
|
+
if (error.isNetworkError) {
|
|
327
|
+
showToast('No internet connection');
|
|
328
|
+
} else if (error.isAuthError) {
|
|
329
|
+
// SDK already triggers onAuthFailure — this rarely fires
|
|
330
|
+
showToast('Please log in again');
|
|
331
|
+
} else if (error.statusCode === 429) {
|
|
332
|
+
showToast('Too many swipes! Slow down.');
|
|
333
|
+
} else {
|
|
334
|
+
showToast(error.message); // Server error message
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Twilio Calling System
|
|
343
|
+
|
|
344
|
+
### Architecture
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
Mobile/Web App
|
|
348
|
+
│
|
|
349
|
+
├─ POST /calls/video/initiate ───┐
|
|
350
|
+
├─ POST /calls/video/accept ───┤
|
|
351
|
+
├─ POST /calls/audio/initiate ───┤ Authenticated
|
|
352
|
+
├─ GET /calls/audio/token ───┤ API Routes
|
|
353
|
+
├─ GET /calls/history ───┘
|
|
354
|
+
│
|
|
355
|
+
│ ┌──────────────────────────────┐
|
|
356
|
+
│ │ CallController │ Validates input
|
|
357
|
+
│ └─────────────┬────────────────┘
|
|
358
|
+
│ │
|
|
359
|
+
│ ┌─────────────▼────────────────┐
|
|
360
|
+
│ │ CallService │ Business logic + DB
|
|
361
|
+
│ └─────────────┬────────────────┘
|
|
362
|
+
│ │
|
|
363
|
+
│ ┌─────────────▼────────────────┐
|
|
364
|
+
│ │ TwilioService │ Twilio SDK calls
|
|
365
|
+
│ └─────────────┬────────────────┘
|
|
366
|
+
│ │
|
|
367
|
+
│ ▼
|
|
368
|
+
│ Twilio Cloud
|
|
369
|
+
│ │
|
|
370
|
+
│ ┌─────────────▼────────────────┐
|
|
371
|
+
│ │ Webhook Endpoints │ Status callbacks
|
|
372
|
+
│ │ /webhooks/twilio/video-* │ (signature validated)
|
|
373
|
+
│ │ /webhooks/twilio/voice-* │
|
|
374
|
+
│ └──────────────────────────────┘
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Video Call Flow
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
Caller Backend Twilio Receiver
|
|
381
|
+
│ │ │ │
|
|
382
|
+
├── POST /video/initiate ──▶ │ │
|
|
383
|
+
│ ├── Create Room ──────────▶│ │
|
|
384
|
+
│ ├── Save Call (RINGING) ───▶ DB │
|
|
385
|
+
│ ├── Generate Token ────────▶│ │
|
|
386
|
+
│ ◀── { callId, token } ──┤ │ │
|
|
387
|
+
│ ├── Push Notification ─────────────────────────────▶│
|
|
388
|
+
│ │ │ │
|
|
389
|
+
│ Connect to Room ─────────────────────────────────▶│ │
|
|
390
|
+
│ │ │ │
|
|
391
|
+
│ │ │ POST /video/accept ──┤
|
|
392
|
+
│ │ ◀── Accept Call ────────┤ │
|
|
393
|
+
│ ├── Update (IN_PROGRESS) ──▶ DB │
|
|
394
|
+
│ ├── Generate Token ────────▶│ │
|
|
395
|
+
│ │ { token } ──────────────────────────────────────▶│
|
|
396
|
+
│ │ │ │
|
|
397
|
+
│ ◀════════════ Video Connection Established ════════════════════════════════▶│
|
|
398
|
+
│ │ │ │
|
|
399
|
+
│── POST /video/end ──────▶│ │ │
|
|
400
|
+
│ ├── End Room ─────────────▶│ │
|
|
401
|
+
│ ├── Update (COMPLETED) ────▶ DB │
|
|
402
|
+
│ ◀── { duration } ───────┤ │ │
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Audio Call Flow
|
|
406
|
+
|
|
407
|
+
Audio calls use **Twilio Client SDK** (not Video rooms). The flow:
|
|
408
|
+
|
|
409
|
+
1. Both users fetch voice tokens via `GET /calls/audio/token` when entering a call-capable screen
|
|
410
|
+
2. Caller hits `POST /calls/audio/initiate` — backend creates a `Call` record with status `RINGING`
|
|
411
|
+
3. Caller's Twilio Client SDK makes an outbound call, which triggers the TwiML App webhook
|
|
412
|
+
4. Backend's `/webhooks/twilio/voice-incoming` returns TwiML that dials the receiver's identity
|
|
413
|
+
5. Receiver's Twilio Client SDK receives the incoming call
|
|
414
|
+
6. Status callbacks update the `Call` record throughout the lifecycle
|
|
415
|
+
|
|
416
|
+
### Twilio Setup Checklist
|
|
417
|
+
|
|
418
|
+
Before calling works, set up these Twilio resources:
|
|
419
|
+
|
|
420
|
+
1. **Twilio Account** — [console.twilio.com](https://console.twilio.com)
|
|
421
|
+
2. **API Key** — Console > Account > Keys > Create Standard Key
|
|
422
|
+
- Copy the SID (starts with `SK`) → `TWILIO_API_KEY_SID`
|
|
423
|
+
- Copy the Secret → `TWILIO_API_KEY_SECRET`
|
|
424
|
+
3. **TwiML App** — Console > Voice > TwiML Apps > Create
|
|
425
|
+
- Voice Request URL: `https://api.clinchdate.com/api/v1/webhooks/twilio/voice-incoming`
|
|
426
|
+
- Copy the SID (starts with `AP`) → `TWILIO_TWIML_APP_SID`
|
|
427
|
+
4. **Phone Number** (optional, for PSTN) — Console > Phone Numbers > Buy a Number
|
|
428
|
+
|
|
429
|
+
### Stale Call Cleanup
|
|
430
|
+
|
|
431
|
+
A background job runs every 2 minutes (configurable via `CALL_RINGING_TIMEOUT_MINUTES`) to clean up calls stuck in `RINGING` status. These are calls where the receiver never answered — they get moved to `NO_ANSWER` status, and any associated Twilio rooms are ended.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Database Models
|
|
436
|
+
|
|
437
|
+
### 21 Models (20 existing + 1 new)
|
|
438
|
+
|
|
439
|
+
| Model | Collection | Purpose | Status |
|
|
440
|
+
|-------|-----------|---------|--------|
|
|
441
|
+
| User | users | Core user accounts, auth | ✅ Fixed |
|
|
442
|
+
| Admin | admins | Admin panel users | ✅ Fixed |
|
|
443
|
+
| Profile | profiles | User dating profiles | ✅ Fixed |
|
|
444
|
+
| Match | matches | Mutual matches between users | ✅ Fixed |
|
|
445
|
+
| Swipe | swipes | Like/pass/super-like actions | ✅ Fixed |
|
|
446
|
+
| Conversation | conversations | Chat conversation metadata | ✅ Fixed |
|
|
447
|
+
| Message | messages | Individual chat messages | ✅ Fixed |
|
|
448
|
+
| Chat | chats | ⚠️ Duplicate messaging system | ✅ Fixed |
|
|
449
|
+
| **Call** | **calls** | **★ Video & audio call records** | **NEW** |
|
|
450
|
+
| Payment | payments | Stripe payment records | ✅ Fixed |
|
|
451
|
+
| Subscription | subscriptions | Plus/Platinum plans | ✅ Fixed |
|
|
452
|
+
| Boost | boosts | Profile visibility boosts | ✅ Fixed |
|
|
453
|
+
| Event | events | Community events | ✅ Fixed |
|
|
454
|
+
| Notification | notifications | Push/email/SMS notifications | ✅ Fixed |
|
|
455
|
+
| Report | reports | User safety reports | ✅ Fixed |
|
|
456
|
+
| SupportTicket | support_tickets | Customer support | ✅ Fixed |
|
|
457
|
+
| Announcement | announcements | Admin broadcasts | ✅ Fixed |
|
|
458
|
+
| BlockedUser | blocked_users | User blocking | ✅ Fixed |
|
|
459
|
+
| Verification | verifications | Identity verification | ✅ Fixed |
|
|
460
|
+
| Withdrawal | withdrawals | ⚠️ Missing userId field | ✅ Fixed |
|
|
461
|
+
| Settings | settings | User preferences | ✅ Clean |
|
|
462
|
+
|
|
463
|
+
### Index Strategy
|
|
464
|
+
|
|
465
|
+
All models now use **explicit `schema.index()` calls** as the single source of truth. No inline `index: true` declarations. This eliminates the ~25 duplicate index warnings that were appearing at startup.
|
|
466
|
+
|
|
467
|
+
**Rules applied:**
|
|
468
|
+
- `unique: true` on a field already creates an index — no separate index needed
|
|
469
|
+
- Single-field indexes that are prefixes of existing compound indexes were removed
|
|
470
|
+
- `sparse: true` preserved on optional unique fields to allow multiple nulls
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## API Routes Reference
|
|
475
|
+
|
|
476
|
+
### Authentication — `/api/v1/auth`
|
|
477
|
+
|
|
478
|
+
| Method | Path | Description |
|
|
479
|
+
|--------|------|-------------|
|
|
480
|
+
| POST | /register | Create account |
|
|
481
|
+
| POST | /login | Email/password login |
|
|
482
|
+
| POST | /firebase-login | Firebase social login |
|
|
483
|
+
| POST | /refresh-token | Refresh JWT |
|
|
484
|
+
| POST | /forgot-password | Send reset email |
|
|
485
|
+
| POST | /reset-password | Reset with token |
|
|
486
|
+
| POST | /verify-phone | Send SMS verification |
|
|
487
|
+
| POST | /confirm-phone | Confirm SMS code |
|
|
488
|
+
| POST | /logout | Invalidate tokens |
|
|
489
|
+
|
|
490
|
+
### Users & Profiles — `/api/v1/users`, `/api/v1/profiles`
|
|
491
|
+
|
|
492
|
+
| Method | Path | Description |
|
|
493
|
+
|--------|------|-------------|
|
|
494
|
+
| GET | /users/me | Current user |
|
|
495
|
+
| PUT | /users/me | Update user |
|
|
496
|
+
| DELETE | /users/me | Soft delete account |
|
|
497
|
+
| GET | /profiles/:id | View profile |
|
|
498
|
+
| PUT | /profiles/me | Update profile |
|
|
499
|
+
| POST | /profiles/photos | Upload photos |
|
|
500
|
+
| DELETE | /profiles/photos/:id | Remove photo |
|
|
501
|
+
|
|
502
|
+
### Matching — `/api/v1/matches`, `/api/v1/swipes`
|
|
503
|
+
|
|
504
|
+
| Method | Path | Description |
|
|
505
|
+
|--------|------|-------------|
|
|
506
|
+
| GET | /matches | List matches |
|
|
507
|
+
| GET | /matches/discover | Discovery feed |
|
|
508
|
+
| POST | /swipes | Swipe (like/pass/super-like) |
|
|
509
|
+
| DELETE | /matches/:id | Unmatch |
|
|
510
|
+
|
|
511
|
+
### Calls — `/api/v1/calls` (★ NEW)
|
|
512
|
+
|
|
513
|
+
| Method | Path | Auth | Description |
|
|
514
|
+
|--------|------|------|-------------|
|
|
515
|
+
| POST | /video/initiate | ✅ | Start video call |
|
|
516
|
+
| POST | /video/accept | ✅ | Accept video call |
|
|
517
|
+
| POST | /video/reject | ✅ | Reject video call |
|
|
518
|
+
| POST | /video/end | ✅ | End video call |
|
|
519
|
+
| GET | /video/status/:callId | ✅ | Room status |
|
|
520
|
+
| GET | /video/active-rooms | ✅ | Admin: list active rooms |
|
|
521
|
+
| POST | /audio/initiate | ✅ | Start audio call |
|
|
522
|
+
| POST | /audio/accept | ✅ | Accept audio call |
|
|
523
|
+
| POST | /audio/reject | ✅ | Reject audio call |
|
|
524
|
+
| POST | /audio/end | ✅ | End audio call |
|
|
525
|
+
| GET | /audio/token | ✅ | Get Twilio voice token |
|
|
526
|
+
| GET | /history | ✅ | Call history |
|
|
527
|
+
| POST | /report-quality | ✅ | Report call quality |
|
|
528
|
+
| GET | /recordings/:callId | ✅ | Get recordings |
|
|
529
|
+
|
|
530
|
+
### Webhooks — `/api/v1/webhooks/twilio` (★ NEW)
|
|
531
|
+
|
|
532
|
+
| Method | Path | Auth | Description |
|
|
533
|
+
|--------|------|------|-------------|
|
|
534
|
+
| POST | /video-status | Twilio Sig | Video room events |
|
|
535
|
+
| POST | /voice-status | Twilio Sig | Voice call events |
|
|
536
|
+
| POST | /voice-incoming | Twilio Sig | TwiML App handler |
|
|
537
|
+
| POST | /voice-complete | Twilio Sig | Dial action complete |
|
|
538
|
+
| POST | /voice-fallback | Twilio Sig | Error fallback |
|
|
539
|
+
|
|
540
|
+
### Payments — `/api/v1/payments`, `/api/v1/subscriptions`
|
|
541
|
+
|
|
542
|
+
| Method | Path | Description |
|
|
543
|
+
|--------|------|-------------|
|
|
544
|
+
| POST | /payments/create-intent | Stripe PaymentIntent |
|
|
545
|
+
| POST | /payments/confirm | Confirm payment |
|
|
546
|
+
| GET | /subscriptions/plans | List plans |
|
|
547
|
+
| POST | /subscriptions/subscribe | Start subscription |
|
|
548
|
+
| POST | /subscriptions/cancel | Cancel subscription |
|
|
549
|
+
| POST | /webhooks/stripe | Stripe webhooks |
|
|
550
|
+
|
|
551
|
+
### Other Endpoints
|
|
552
|
+
|
|
553
|
+
| Prefix | Purpose |
|
|
554
|
+
|--------|---------|
|
|
555
|
+
| `/api/v1/conversations` | Chat conversations |
|
|
556
|
+
| `/api/v1/messages` | Chat messages |
|
|
557
|
+
| `/api/v1/events` | Community events |
|
|
558
|
+
| `/api/v1/notifications` | Notification preferences |
|
|
559
|
+
| `/api/v1/reports` | User safety reports |
|
|
560
|
+
| `/api/v1/support` | Support tickets |
|
|
561
|
+
| `/api/v1/admin` | Admin panel operations |
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Environment Setup
|
|
566
|
+
|
|
567
|
+
### Backend `.env`
|
|
568
|
+
|
|
569
|
+
The `.env` file includes **all original variables** from `.env.example` plus the new Twilio calling variables. Key sections:
|
|
570
|
+
|
|
571
|
+
| Section | Key Variables | Where to Get Them |
|
|
572
|
+
|---------|--------------|-------------------|
|
|
573
|
+
| Database | `MONGODB_URI` | MongoDB Atlas Console |
|
|
574
|
+
| Auth | `JWT_SECRET`, `JWT_REFRESH_SECRET` | Generate with `crypto.randomBytes(64)` |
|
|
575
|
+
| Firebase | `FIREBASE_PROJECT_ID`, `FIREBASE_PRIVATE_KEY` | Firebase Console > Service Accounts |
|
|
576
|
+
| AWS | `AWS_ACCESS_KEY_ID`, `AWS_S3_BUCKET` | AWS IAM Console |
|
|
577
|
+
| Stripe | `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` | Stripe Dashboard > Developers |
|
|
578
|
+
| Twilio SMS | `TWILIO_ACCOUNT_SID`, `TWILIO_VERIFY_SERVICE_SID` | Twilio Console |
|
|
579
|
+
| **Twilio Calling** | `TWILIO_API_KEY_SID`, `TWILIO_TWIML_APP_SID` | **See Twilio Setup Checklist above** |
|
|
580
|
+
| SendGrid | `SENDGRID_API_KEY` | SendGrid Dashboard |
|
|
581
|
+
| Cloudinary | `CLOUDINARY_CLOUD_NAME` | Cloudinary Dashboard |
|
|
582
|
+
| Google | `GOOGLE_MAPS_API_KEY` | Google Cloud Console |
|
|
583
|
+
|
|
584
|
+
### Production Overrides
|
|
585
|
+
|
|
586
|
+
For production deployment, these **must** be changed:
|
|
587
|
+
|
|
588
|
+
```env
|
|
589
|
+
NODE_ENV=production
|
|
590
|
+
JWT_SECRET=<64-byte-random-hex>
|
|
591
|
+
JWT_REFRESH_SECRET=<64-byte-random-hex>
|
|
592
|
+
ADMIN_SECRET_KEY=<64-byte-random-hex>
|
|
593
|
+
ADMIN_DEFAULT_PASSWORD=<strong-unique-password>
|
|
594
|
+
TWILIO_WEBHOOK_VALIDATE=true # MUST be true in production
|
|
595
|
+
API_BASE_URL=https://api.clinchdate.com
|
|
596
|
+
CORS_ALLOWED_ORIGINS=https://clinchdate.com,https://admin.clinchdate.com
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
## Deployment
|
|
602
|
+
|
|
603
|
+
| Component | Platform | Build Command |
|
|
604
|
+
|-----------|----------|--------------|
|
|
605
|
+
| Backend API | Render | `npm run build && npm start` |
|
|
606
|
+
| Web App | Vercel | `npm run build` |
|
|
607
|
+
| Admin Panel | Vercel | `npm run build` |
|
|
608
|
+
| Mobile (Android) | Play Store | `eas build --platform android` |
|
|
609
|
+
| Mobile (iOS) | App Store | `eas build --platform ios` |
|
|
610
|
+
| API Client SDK | npm | `npm run build && npm publish` |
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## Work Completed
|
|
615
|
+
|
|
616
|
+
### Session 1 — Backend & SDK Fixes
|
|
617
|
+
|
|
618
|
+
| # | Task | Files | Impact |
|
|
619
|
+
|---|------|-------|--------|
|
|
620
|
+
| 1 | **API Client SDK syntax fix** | `call.service.ts` | Fixed missing `<` on line 169, eliminating all 28 cascading TypeScript errors |
|
|
621
|
+
| 2 | **Mongoose duplicate index cleanup** | 20 model files | Removed all inline `index: true`, kept explicit `schema.index()` as single source of truth |
|
|
622
|
+
|
|
623
|
+
### Session 2 — Twilio Calling System + Backend Audit
|
|
624
|
+
|
|
625
|
+
| # | Task | Files | Impact |
|
|
626
|
+
|---|------|-------|--------|
|
|
627
|
+
| 3 | **Call.model.ts** | 1 new model | Full call lifecycle persistence — participants, quality reports, recordings, stale cleanup |
|
|
628
|
+
| 4 | **twilio.config.ts** | 1 new config | Centralized Twilio env validation — fails fast at startup if anything is missing |
|
|
629
|
+
| 5 | **twilio.service.ts** | 1 new service | Twilio SDK wrapper — room creation, tokens (Video + Voice), TwiML generation, recordings, signature validation |
|
|
630
|
+
| 6 | **call.service.ts** | 1 new service | Business logic orchestration — match verification, active call checks, token generation, webhook processing, stale cleanup |
|
|
631
|
+
| 7 | **call.controller.ts** | 1 new controller | Express handlers for all 14 call API endpoints |
|
|
632
|
+
| 8 | **twilio-webhook.controller.ts** | 1 new controller | 5 webhook handlers with proper TwiML responses and error resilience |
|
|
633
|
+
| 9 | **twilio-webhook.middleware.ts** | 1 new middleware | Signature validation to prevent webhook spoofing |
|
|
634
|
+
| 10 | **call.routes.ts** | 1 new route | Authenticated routes for `/api/v1/calls/*` |
|
|
635
|
+
| 11 | **webhook.routes.ts** | 1 new route | Twilio webhook routes with `urlencoded` body parsing |
|
|
636
|
+
| 12 | **call.setup.ts** | 1 new bootstrap | One-line wiring: `setupCallSystem(app)` — creates everything and starts cleanup job |
|
|
637
|
+
| 13 | **Backend Audit** | 1 report | 5 critical, 7 important, 4 nice-to-have findings |
|
|
638
|
+
|
|
639
|
+
### Session 3 — SDK Core + Env + Documentation
|
|
640
|
+
|
|
641
|
+
| # | Task | Files | Impact |
|
|
642
|
+
|---|------|-------|--------|
|
|
643
|
+
| 14 | **Backend .env** | 1 env file | Complete env with all original vars + Twilio calling additions |
|
|
644
|
+
| 15 | **SDK config.ts** | 1 new file | Configuration singleton with validation, platform-specific examples |
|
|
645
|
+
| 16 | **SDK client.ts** | 1 new file | Production HTTP client — auto-auth, silent refresh, request queuing, retry with backoff, error normalization, upload progress, response unwrapping |
|
|
646
|
+
| 17 | **README.md** | 1 doc | Complete technical documentation |
|
|
647
|
+
|
|
648
|
+
**Total: 33 files delivered across 3 sessions**
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
## Remaining Work
|
|
653
|
+
|
|
654
|
+
### Priority 1 — Must Fix Before Launch
|
|
655
|
+
|
|
656
|
+
| Task | Effort | Details |
|
|
657
|
+
|------|--------|---------|
|
|
658
|
+
| Fix Withdrawal model (add `userId`) | 5 min | Add field + index |
|
|
659
|
+
| Resolve Chat vs Message duplication | 30 min | Pick one, remove the other |
|
|
660
|
+
| Add rate limiting | 1 hr | `express-rate-limit` + Redis |
|
|
661
|
+
| Add input validation (zod/joi) | 2-3 hr | Schema validation middleware |
|
|
662
|
+
| Harden Stripe webhooks | 1 hr | Signature validation + raw body |
|
|
663
|
+
|
|
664
|
+
### Priority 2 — Frontend Integration
|
|
665
|
+
|
|
666
|
+
| Task | Effort | Details |
|
|
667
|
+
|------|--------|---------|
|
|
668
|
+
| Mobile: Light/dark theme system | 3-4 hr | Theme context, tokens, AsyncStorage persistence |
|
|
669
|
+
| Mobile: Wire call screens to SDK | 2-3 hr | Connect to Twilio Video/Voice SDKs |
|
|
670
|
+
| Web/Mobile: Implement i18n | 3-4 hr | 5 languages (EN, FR, DE, ES, PT) |
|
|
671
|
+
| Web/Mobile: Wire all API calls | 4-6 hr | Replace mocks with SDK service calls |
|
|
672
|
+
| Admin: Implement RBAC | 2-3 hr | Role-based access on admin routes |
|
|
673
|
+
|
|
674
|
+
### Priority 3 — Polish
|
|
675
|
+
|
|
676
|
+
| Task | Effort | Details |
|
|
677
|
+
|------|--------|---------|
|
|
678
|
+
| Fix Profile `incrementViews` race condition | 5 min | Use `$inc` instead of `.save()` |
|
|
679
|
+
| Add soft-delete filter for Users | 30 min | Auto-filter `deletedAt: null` |
|
|
680
|
+
| Add TTL index for Boost expiration | 5 min | `{ expireAfterSeconds: 0 }` |
|
|
681
|
+
| Compound index optimization | 30 min | Remove redundant single-field indexes |
|
|
682
|
+
|
|
683
|
+
---
|
|
684
|
+
|
|
685
|
+
## Known Issues & Audit Findings
|
|
686
|
+
|
|
687
|
+
### 🔴 Critical
|
|
688
|
+
|
|
689
|
+
1. **Chat vs Message model duplication** — Two separate messaging systems exist. `Chat.model.ts` uses `matchId` + `senderId/recipientId`. `Message.model.ts` + `Conversation.model.ts` uses `conversationId` + `senderId/receiverId`. Pick one and remove the other.
|
|
690
|
+
|
|
691
|
+
2. **Withdrawal.model.ts missing userId** — No way to associate withdrawals with users. Add `userId: ObjectId` field with an index.
|
|
692
|
+
|
|
693
|
+
### 🟡 Important
|
|
694
|
+
|
|
695
|
+
3. **No rate limiting** — Login, register, password reset endpoints are wide open to brute force.
|
|
696
|
+
4. **No input validation middleware** — Controllers do basic null checks but no schema validation.
|
|
697
|
+
5. **Profile.incrementViews race condition** — Uses `.save()` under concurrency. Use `$inc` atomic update.
|
|
698
|
+
6. **Soft-deleted users visible in queries** — Match, Chat, and other models don't filter out `deletedAt` users.
|
|
699
|
+
|
|
700
|
+
### 🟢 Minor
|
|
701
|
+
|
|
702
|
+
7. **Inconsistent lean typing** — Some statics use `as unknown as Type[]`, should use `.lean<Type[]>()`
|
|
703
|
+
8. **No cursor-based pagination** — List endpoints use `.skip()` which degrades at scale
|
|
704
|
+
9. **Boost model lacks TTL index** — Expired boosts remain in the database indefinitely
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
*Last updated: February 2026*
|
|
709
|
+
*Architecture: Adrian Ebesoh — CodersHub Innovations*
|
|
710
|
+
|
|
711
|
+
|