servcraft 0.1.0 → 0.1.3

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.
Files changed (217) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/.github/CODEOWNERS +18 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
  4. package/.github/dependabot.yml +59 -0
  5. package/.github/workflows/ci.yml +188 -0
  6. package/.github/workflows/release.yml +195 -0
  7. package/AUDIT.md +602 -0
  8. package/LICENSE +21 -0
  9. package/README.md +1102 -1
  10. package/dist/cli/index.cjs +2026 -2168
  11. package/dist/cli/index.cjs.map +1 -1
  12. package/dist/cli/index.js +2026 -2168
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/index.cjs +595 -616
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +114 -52
  17. package/dist/index.d.ts +114 -52
  18. package/dist/index.js +595 -616
  19. package/dist/index.js.map +1 -1
  20. package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
  21. package/docs/DATABASE_MULTI_ORM.md +399 -0
  22. package/docs/PHASE1_BREAKDOWN.md +346 -0
  23. package/docs/PROGRESS.md +550 -0
  24. package/docs/modules/ANALYTICS.md +226 -0
  25. package/docs/modules/API-VERSIONING.md +252 -0
  26. package/docs/modules/AUDIT.md +192 -0
  27. package/docs/modules/AUTH.md +431 -0
  28. package/docs/modules/CACHE.md +346 -0
  29. package/docs/modules/EMAIL.md +254 -0
  30. package/docs/modules/FEATURE-FLAG.md +291 -0
  31. package/docs/modules/I18N.md +294 -0
  32. package/docs/modules/MEDIA-PROCESSING.md +281 -0
  33. package/docs/modules/MFA.md +266 -0
  34. package/docs/modules/NOTIFICATION.md +311 -0
  35. package/docs/modules/OAUTH.md +237 -0
  36. package/docs/modules/PAYMENT.md +804 -0
  37. package/docs/modules/QUEUE.md +540 -0
  38. package/docs/modules/RATE-LIMIT.md +339 -0
  39. package/docs/modules/SEARCH.md +288 -0
  40. package/docs/modules/SECURITY.md +327 -0
  41. package/docs/modules/SESSION.md +382 -0
  42. package/docs/modules/SWAGGER.md +305 -0
  43. package/docs/modules/UPLOAD.md +296 -0
  44. package/docs/modules/USER.md +505 -0
  45. package/docs/modules/VALIDATION.md +294 -0
  46. package/docs/modules/WEBHOOK.md +270 -0
  47. package/docs/modules/WEBSOCKET.md +691 -0
  48. package/package.json +53 -38
  49. package/prisma/schema.prisma +395 -1
  50. package/src/cli/commands/add-module.ts +520 -87
  51. package/src/cli/commands/db.ts +3 -4
  52. package/src/cli/commands/docs.ts +256 -6
  53. package/src/cli/commands/generate.ts +12 -19
  54. package/src/cli/commands/init.ts +384 -214
  55. package/src/cli/index.ts +0 -4
  56. package/src/cli/templates/repository.ts +6 -1
  57. package/src/cli/templates/routes.ts +6 -21
  58. package/src/cli/utils/docs-generator.ts +6 -7
  59. package/src/cli/utils/env-manager.ts +717 -0
  60. package/src/cli/utils/field-parser.ts +16 -7
  61. package/src/cli/utils/interactive-prompt.ts +223 -0
  62. package/src/cli/utils/template-manager.ts +346 -0
  63. package/src/config/database.config.ts +183 -0
  64. package/src/config/env.ts +0 -10
  65. package/src/config/index.ts +0 -14
  66. package/src/core/server.ts +1 -1
  67. package/src/database/adapters/mongoose.adapter.ts +132 -0
  68. package/src/database/adapters/prisma.adapter.ts +118 -0
  69. package/src/database/connection.ts +190 -0
  70. package/src/database/interfaces/database.interface.ts +85 -0
  71. package/src/database/interfaces/index.ts +7 -0
  72. package/src/database/interfaces/repository.interface.ts +129 -0
  73. package/src/database/models/mongoose/index.ts +7 -0
  74. package/src/database/models/mongoose/payment.schema.ts +347 -0
  75. package/src/database/models/mongoose/user.schema.ts +154 -0
  76. package/src/database/prisma.ts +1 -4
  77. package/src/database/redis.ts +101 -0
  78. package/src/database/repositories/mongoose/index.ts +7 -0
  79. package/src/database/repositories/mongoose/payment.repository.ts +380 -0
  80. package/src/database/repositories/mongoose/user.repository.ts +255 -0
  81. package/src/database/seed.ts +6 -1
  82. package/src/index.ts +9 -20
  83. package/src/middleware/security.ts +2 -6
  84. package/src/modules/analytics/analytics.routes.ts +80 -0
  85. package/src/modules/analytics/analytics.service.ts +364 -0
  86. package/src/modules/analytics/index.ts +18 -0
  87. package/src/modules/analytics/types.ts +180 -0
  88. package/src/modules/api-versioning/index.ts +15 -0
  89. package/src/modules/api-versioning/types.ts +86 -0
  90. package/src/modules/api-versioning/versioning.middleware.ts +120 -0
  91. package/src/modules/api-versioning/versioning.routes.ts +54 -0
  92. package/src/modules/api-versioning/versioning.service.ts +189 -0
  93. package/src/modules/audit/audit.repository.ts +206 -0
  94. package/src/modules/audit/audit.service.ts +27 -59
  95. package/src/modules/auth/auth.controller.ts +2 -2
  96. package/src/modules/auth/auth.middleware.ts +3 -9
  97. package/src/modules/auth/auth.routes.ts +10 -107
  98. package/src/modules/auth/auth.service.ts +126 -23
  99. package/src/modules/auth/index.ts +3 -4
  100. package/src/modules/cache/cache.service.ts +367 -0
  101. package/src/modules/cache/index.ts +10 -0
  102. package/src/modules/cache/types.ts +44 -0
  103. package/src/modules/email/email.service.ts +3 -10
  104. package/src/modules/email/templates.ts +2 -8
  105. package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
  106. package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
  107. package/src/modules/feature-flag/feature-flag.service.ts +566 -0
  108. package/src/modules/feature-flag/index.ts +20 -0
  109. package/src/modules/feature-flag/types.ts +192 -0
  110. package/src/modules/i18n/i18n.middleware.ts +186 -0
  111. package/src/modules/i18n/i18n.routes.ts +191 -0
  112. package/src/modules/i18n/i18n.service.ts +456 -0
  113. package/src/modules/i18n/index.ts +18 -0
  114. package/src/modules/i18n/types.ts +118 -0
  115. package/src/modules/media-processing/index.ts +17 -0
  116. package/src/modules/media-processing/media-processing.routes.ts +111 -0
  117. package/src/modules/media-processing/media-processing.service.ts +245 -0
  118. package/src/modules/media-processing/types.ts +156 -0
  119. package/src/modules/mfa/index.ts +20 -0
  120. package/src/modules/mfa/mfa.repository.ts +206 -0
  121. package/src/modules/mfa/mfa.routes.ts +595 -0
  122. package/src/modules/mfa/mfa.service.ts +572 -0
  123. package/src/modules/mfa/totp.ts +150 -0
  124. package/src/modules/mfa/types.ts +57 -0
  125. package/src/modules/notification/index.ts +20 -0
  126. package/src/modules/notification/notification.repository.ts +356 -0
  127. package/src/modules/notification/notification.service.ts +483 -0
  128. package/src/modules/notification/types.ts +119 -0
  129. package/src/modules/oauth/index.ts +20 -0
  130. package/src/modules/oauth/oauth.repository.ts +219 -0
  131. package/src/modules/oauth/oauth.routes.ts +446 -0
  132. package/src/modules/oauth/oauth.service.ts +293 -0
  133. package/src/modules/oauth/providers/apple.provider.ts +250 -0
  134. package/src/modules/oauth/providers/facebook.provider.ts +181 -0
  135. package/src/modules/oauth/providers/github.provider.ts +248 -0
  136. package/src/modules/oauth/providers/google.provider.ts +189 -0
  137. package/src/modules/oauth/providers/twitter.provider.ts +214 -0
  138. package/src/modules/oauth/types.ts +94 -0
  139. package/src/modules/payment/index.ts +19 -0
  140. package/src/modules/payment/payment.repository.ts +733 -0
  141. package/src/modules/payment/payment.routes.ts +390 -0
  142. package/src/modules/payment/payment.service.ts +354 -0
  143. package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
  144. package/src/modules/payment/providers/paypal.provider.ts +190 -0
  145. package/src/modules/payment/providers/stripe.provider.ts +215 -0
  146. package/src/modules/payment/types.ts +140 -0
  147. package/src/modules/queue/cron.ts +438 -0
  148. package/src/modules/queue/index.ts +87 -0
  149. package/src/modules/queue/queue.routes.ts +600 -0
  150. package/src/modules/queue/queue.service.ts +842 -0
  151. package/src/modules/queue/types.ts +222 -0
  152. package/src/modules/queue/workers.ts +366 -0
  153. package/src/modules/rate-limit/index.ts +59 -0
  154. package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
  155. package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
  156. package/src/modules/rate-limit/rate-limit.service.ts +348 -0
  157. package/src/modules/rate-limit/stores/memory.store.ts +165 -0
  158. package/src/modules/rate-limit/stores/redis.store.ts +322 -0
  159. package/src/modules/rate-limit/types.ts +153 -0
  160. package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
  161. package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
  162. package/src/modules/search/adapters/memory.adapter.ts +278 -0
  163. package/src/modules/search/index.ts +21 -0
  164. package/src/modules/search/search.service.ts +234 -0
  165. package/src/modules/search/types.ts +214 -0
  166. package/src/modules/security/index.ts +40 -0
  167. package/src/modules/security/sanitize.ts +223 -0
  168. package/src/modules/security/security-audit.service.ts +388 -0
  169. package/src/modules/security/security.middleware.ts +398 -0
  170. package/src/modules/session/index.ts +3 -0
  171. package/src/modules/session/session.repository.ts +159 -0
  172. package/src/modules/session/session.service.ts +340 -0
  173. package/src/modules/session/types.ts +38 -0
  174. package/src/modules/swagger/index.ts +7 -1
  175. package/src/modules/swagger/schema-builder.ts +16 -4
  176. package/src/modules/swagger/swagger.service.ts +9 -10
  177. package/src/modules/swagger/types.ts +0 -2
  178. package/src/modules/upload/index.ts +14 -0
  179. package/src/modules/upload/types.ts +83 -0
  180. package/src/modules/upload/upload.repository.ts +199 -0
  181. package/src/modules/upload/upload.routes.ts +311 -0
  182. package/src/modules/upload/upload.service.ts +448 -0
  183. package/src/modules/user/index.ts +3 -3
  184. package/src/modules/user/user.controller.ts +15 -9
  185. package/src/modules/user/user.repository.ts +237 -113
  186. package/src/modules/user/user.routes.ts +39 -164
  187. package/src/modules/user/user.service.ts +4 -3
  188. package/src/modules/validation/validator.ts +12 -17
  189. package/src/modules/webhook/index.ts +91 -0
  190. package/src/modules/webhook/retry.ts +196 -0
  191. package/src/modules/webhook/signature.ts +135 -0
  192. package/src/modules/webhook/types.ts +181 -0
  193. package/src/modules/webhook/webhook.repository.ts +358 -0
  194. package/src/modules/webhook/webhook.routes.ts +442 -0
  195. package/src/modules/webhook/webhook.service.ts +457 -0
  196. package/src/modules/websocket/features.ts +504 -0
  197. package/src/modules/websocket/index.ts +106 -0
  198. package/src/modules/websocket/middlewares.ts +298 -0
  199. package/src/modules/websocket/types.ts +181 -0
  200. package/src/modules/websocket/websocket.service.ts +692 -0
  201. package/src/utils/errors.ts +7 -0
  202. package/src/utils/pagination.ts +4 -1
  203. package/tests/helpers/db-check.ts +79 -0
  204. package/tests/integration/auth-redis.test.ts +94 -0
  205. package/tests/integration/cache-redis.test.ts +387 -0
  206. package/tests/integration/mongoose-repositories.test.ts +410 -0
  207. package/tests/integration/payment-prisma.test.ts +637 -0
  208. package/tests/integration/queue-bullmq.test.ts +417 -0
  209. package/tests/integration/user-prisma.test.ts +441 -0
  210. package/tests/integration/websocket-socketio.test.ts +552 -0
  211. package/tests/setup.ts +11 -9
  212. package/vitest.config.ts +3 -8
  213. package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
  214. package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
  215. package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
  216. package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
  217. package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
@@ -0,0 +1,281 @@
1
+ # Media Processing Module
2
+
3
+ Image and video processing with job queue support.
4
+
5
+ ## Features
6
+
7
+ - **Image Processing** - Resize, crop, format conversion
8
+ - **Video Processing** - Transcoding, thumbnail extraction
9
+ - **Job Queue** - Async processing with status tracking
10
+ - **Concurrency Control** - Limit concurrent processing jobs
11
+ - **Media Info** - Extract metadata from media files
12
+
13
+ ## Usage
14
+
15
+ ### Configuration
16
+
17
+ ```typescript
18
+ import { MediaProcessingService } from 'servcraft/modules/media-processing';
19
+
20
+ const mediaService = new MediaProcessingService({
21
+ ffmpegPath: '/usr/bin/ffmpeg',
22
+ ffprobePath: '/usr/bin/ffprobe',
23
+ tempDir: './temp',
24
+ maxConcurrent: 3,
25
+ gpuAcceleration: false,
26
+ });
27
+ ```
28
+
29
+ ### Image Processing
30
+
31
+ ```typescript
32
+ // Process image with operations
33
+ const result = await mediaService.processImage(
34
+ '/uploads/original.jpg',
35
+ '/uploads/processed.jpg',
36
+ [
37
+ { type: 'resize', width: 800, height: 600 },
38
+ { type: 'format', format: 'webp', quality: 85 },
39
+ ]
40
+ );
41
+
42
+ // Result
43
+ // {
44
+ // success: true,
45
+ // outputPath: '/uploads/processed.jpg',
46
+ // processingTime: 245
47
+ // }
48
+ ```
49
+
50
+ ### Video Processing
51
+
52
+ ```typescript
53
+ const result = await mediaService.processVideo(
54
+ '/uploads/video.mp4',
55
+ '/uploads/video-720p.mp4',
56
+ [
57
+ { type: 'resize', width: 1280, height: 720 },
58
+ { type: 'codec', video: 'h264', audio: 'aac' },
59
+ { type: 'bitrate', video: '2000k', audio: '128k' },
60
+ ]
61
+ );
62
+ ```
63
+
64
+ ### Thumbnail Generation
65
+
66
+ ```typescript
67
+ // Generate thumbnail from video
68
+ const result = await mediaService.generateThumbnail(
69
+ '/uploads/video.mp4',
70
+ '/uploads/thumbnail.jpg',
71
+ {
72
+ width: 320,
73
+ height: 180,
74
+ time: '00:00:05', // Extract at 5 seconds
75
+ }
76
+ );
77
+
78
+ // Generate thumbnail from image
79
+ const imageThumb = await mediaService.generateThumbnail(
80
+ '/uploads/image.jpg',
81
+ '/uploads/thumb.jpg',
82
+ { width: 150, height: 150, fit: 'cover' }
83
+ );
84
+ ```
85
+
86
+ ### Media Info
87
+
88
+ ```typescript
89
+ const info = await mediaService.getMediaInfo('/uploads/video.mp4');
90
+ // {
91
+ // path: '/uploads/video.mp4',
92
+ // type: 'video',
93
+ // format: 'mp4',
94
+ // size: 15728640,
95
+ // width: 1920,
96
+ // height: 1080,
97
+ // duration: 120.5,
98
+ // bitrate: 1048576,
99
+ // codec: { video: 'h264', audio: 'aac' },
100
+ // fps: 30
101
+ // }
102
+ ```
103
+
104
+ ### Job Queue
105
+
106
+ ```typescript
107
+ // Create async job
108
+ const job = await mediaService.createJob(
109
+ 'video',
110
+ '/uploads/original.mp4',
111
+ '/uploads/processed.mp4',
112
+ [
113
+ { type: 'resize', width: 1280, height: 720 },
114
+ { type: 'format', format: 'mp4' },
115
+ ]
116
+ );
117
+ // {
118
+ // id: 'job-uuid',
119
+ // status: 'pending',
120
+ // progress: 0,
121
+ // createdAt: Date
122
+ // }
123
+
124
+ // Check job status
125
+ const status = await mediaService.getJob(job.id);
126
+ // {
127
+ // id: 'job-uuid',
128
+ // status: 'processing', // pending | processing | completed | failed
129
+ // progress: 45,
130
+ // ...
131
+ // }
132
+
133
+ // Poll until complete
134
+ while (true) {
135
+ const job = await mediaService.getJob(jobId);
136
+ if (job.status === 'completed' || job.status === 'failed') {
137
+ break;
138
+ }
139
+ await sleep(1000);
140
+ }
141
+ ```
142
+
143
+ ## Image Operations
144
+
145
+ | Operation | Description | Options |
146
+ |-----------|-------------|---------|
147
+ | `resize` | Resize image | `width`, `height`, `fit` |
148
+ | `crop` | Crop region | `x`, `y`, `width`, `height` |
149
+ | `rotate` | Rotate image | `angle` |
150
+ | `flip` | Flip image | `direction` (horizontal/vertical) |
151
+ | `format` | Convert format | `format`, `quality` |
152
+ | `grayscale` | Convert to grayscale | - |
153
+ | `blur` | Apply blur | `sigma` |
154
+ | `sharpen` | Sharpen image | `sigma` |
155
+ | `watermark` | Add watermark | `image`, `position`, `opacity` |
156
+
157
+ ## Video Operations
158
+
159
+ | Operation | Description | Options |
160
+ |-----------|-------------|---------|
161
+ | `resize` | Resize video | `width`, `height` |
162
+ | `trim` | Trim video | `start`, `end` |
163
+ | `codec` | Change codec | `video`, `audio` |
164
+ | `bitrate` | Set bitrate | `video`, `audio` |
165
+ | `fps` | Change framerate | `fps` |
166
+ | `format` | Convert format | `format` |
167
+ | `audio` | Audio options | `remove`, `volume` |
168
+ | `watermark` | Add watermark | `image`, `position` |
169
+
170
+ ## Configuration Types
171
+
172
+ ```typescript
173
+ interface MediaProcessingConfig {
174
+ ffmpegPath?: string; // Path to ffmpeg
175
+ ffprobePath?: string; // Path to ffprobe
176
+ tempDir?: string; // Temporary files directory
177
+ maxConcurrent?: number; // Max concurrent jobs
178
+ gpuAcceleration?: boolean; // Use GPU acceleration
179
+ }
180
+
181
+ interface ProcessingJob {
182
+ id: string;
183
+ mediaType: 'image' | 'video';
184
+ source: string;
185
+ output: string;
186
+ operations: Operation[];
187
+ status: 'pending' | 'processing' | 'completed' | 'failed';
188
+ progress: number;
189
+ error?: string;
190
+ createdAt: Date;
191
+ completedAt?: Date;
192
+ processingTime?: number;
193
+ }
194
+
195
+ interface ThumbnailOptions {
196
+ width?: number;
197
+ height?: number;
198
+ time?: string; // For video: timestamp
199
+ fit?: 'cover' | 'contain' | 'fill';
200
+ }
201
+ ```
202
+
203
+ ## Fastify Integration
204
+
205
+ ```typescript
206
+ import fastifyMultipart from '@fastify/multipart';
207
+
208
+ fastify.post('/api/process/image', async (request, reply) => {
209
+ const file = await request.file();
210
+ const { width, height, format } = request.query;
211
+
212
+ const inputPath = `/tmp/${file.filename}`;
213
+ const outputPath = `/uploads/processed-${Date.now()}.${format || 'jpg'}`;
214
+
215
+ // Save uploaded file
216
+ await saveFile(file, inputPath);
217
+
218
+ // Process image
219
+ const operations = [];
220
+ if (width || height) {
221
+ operations.push({ type: 'resize', width: +width, height: +height });
222
+ }
223
+ if (format) {
224
+ operations.push({ type: 'format', format });
225
+ }
226
+
227
+ const result = await mediaService.processImage(inputPath, outputPath, operations);
228
+
229
+ return result;
230
+ });
231
+
232
+ // Async job endpoint
233
+ fastify.post('/api/process/video', async (request, reply) => {
234
+ const { source, operations } = request.body;
235
+
236
+ const job = await mediaService.createJob(
237
+ 'video',
238
+ source,
239
+ `/uploads/processed-${Date.now()}.mp4`,
240
+ operations
241
+ );
242
+
243
+ return { jobId: job.id, status: job.status };
244
+ });
245
+
246
+ // Job status endpoint
247
+ fastify.get('/api/jobs/:id', async (request, reply) => {
248
+ const job = await mediaService.getJob(request.params.id);
249
+
250
+ if (!job) {
251
+ return reply.status(404).send({ error: 'Job not found' });
252
+ }
253
+
254
+ return job;
255
+ });
256
+ ```
257
+
258
+ ## Production Setup
259
+
260
+ For production, install required libraries:
261
+
262
+ ```bash
263
+ # Image processing
264
+ npm install sharp
265
+
266
+ # Video processing
267
+ npm install fluent-ffmpeg
268
+
269
+ # System dependencies
270
+ apt-get install ffmpeg # Ubuntu/Debian
271
+ brew install ffmpeg # macOS
272
+ ```
273
+
274
+ ## Best Practices
275
+
276
+ 1. **Async Processing** - Use job queue for large files
277
+ 2. **Temp Cleanup** - Clean up temporary files
278
+ 3. **Size Limits** - Set appropriate file size limits
279
+ 4. **Format Validation** - Validate input formats
280
+ 5. **Error Handling** - Handle processing failures gracefully
281
+ 6. **Resource Limits** - Control concurrent jobs
@@ -0,0 +1,266 @@
1
+ # MFA Module
2
+
3
+ Multi-Factor Authentication with support for TOTP, SMS, Email, and Backup Codes.
4
+
5
+ ## Features
6
+
7
+ - **TOTP** - Time-based One-Time Password (Google Authenticator compatible)
8
+ - **SMS** - SMS-based verification codes
9
+ - **Email** - Email-based verification codes
10
+ - **Backup Codes** - Single-use recovery codes
11
+ - **Account Lockout** - Protection against brute-force attacks
12
+ - **Redis Challenges** - Temporary challenge storage with TTL
13
+
14
+ ## Architecture
15
+
16
+ ```
17
+ ┌─────────────────────────────────────────────────────────────┐
18
+ │ MFA Service │
19
+ ├─────────────────────────────────────────────────────────────┤
20
+ │ TOTP Setup │ SMS/Email │ Backup Codes │ Verify │
21
+ └────────┬───────┴───────┬───────┴───────┬────────┴─────┬─────┘
22
+ │ │ │ │
23
+ ▼ ▼ ▼ ▼
24
+ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ ┌───────────┐
25
+ │ Prisma │ │ Redis │ │ Prisma │ │ Redis │
26
+ │ (Settings) │ │ (Challenge) │ │ (Codes) │ │ (Attempts)│
27
+ └─────────────┘ └─────────────┘ └───────────┘ └───────────┘
28
+ ```
29
+
30
+ ## Storage
31
+
32
+ | Data | Storage | TTL |
33
+ |------|---------|-----|
34
+ | User MFA Settings | PostgreSQL (Prisma) | Permanent |
35
+ | TOTP Secrets | PostgreSQL (Prisma) | Permanent |
36
+ | Backup Codes | PostgreSQL (Prisma) | Permanent |
37
+ | Active Challenges | Redis | 5 minutes |
38
+ | Failed Attempts | Redis | 15 minutes |
39
+
40
+ ## Usage
41
+
42
+ ### Basic Setup
43
+
44
+ ```typescript
45
+ import { getMFAService, createMFAService } from 'servcraft/modules/mfa';
46
+
47
+ // Use default configuration
48
+ const mfa = getMFAService();
49
+
50
+ // Or create with custom config
51
+ const mfa = createMFAService({
52
+ issuer: 'MyApp',
53
+ totpWindow: 1,
54
+ backupCodesCount: 10,
55
+ });
56
+ ```
57
+
58
+ ### TOTP Setup
59
+
60
+ ```typescript
61
+ // 1. Initiate TOTP setup
62
+ const setup = await mfa.setupTOTP(userId, userEmail);
63
+ // Returns: { secret, qrCode, manualEntry, uri }
64
+
65
+ // 2. Display QR code to user (setup.qrCode is a data URL)
66
+ // User scans with Google Authenticator
67
+
68
+ // 3. Verify initial setup with a code from the app
69
+ const verified = await mfa.verifyTOTPSetup(userId, '123456');
70
+
71
+ // 4. Later, verify during login
72
+ const result = await mfa.verifyChallenge(userId, '123456', 'totp');
73
+ if (result.success) {
74
+ // Allow login
75
+ }
76
+ ```
77
+
78
+ ### SMS MFA
79
+
80
+ ```typescript
81
+ // 1. Setup SMS (sends verification code)
82
+ await mfa.setupSMS(userId, '+1234567890');
83
+
84
+ // 2. Verify phone number
85
+ const verified = await mfa.verifySMSSetup(userId, '123456');
86
+
87
+ // 3. During login, create challenge
88
+ const challenge = await mfa.createChallenge(userId, 'sms');
89
+ // SMS is sent automatically
90
+
91
+ // 4. Verify the code user received
92
+ const result = await mfa.verifyChallenge(userId, code, 'sms', challenge.id);
93
+ ```
94
+
95
+ ### Email MFA
96
+
97
+ ```typescript
98
+ // 1. Setup Email MFA
99
+ await mfa.setupEmail(userId, 'user@example.com');
100
+
101
+ // 2. Verify email
102
+ const verified = await mfa.verifyEmailSetup(userId, '123456');
103
+
104
+ // 3. During login
105
+ const challenge = await mfa.createChallenge(userId, 'email');
106
+ const result = await mfa.verifyChallenge(userId, code, 'email', challenge.id);
107
+ ```
108
+
109
+ ### Backup Codes
110
+
111
+ ```typescript
112
+ // Generate backup codes (display once to user)
113
+ const { codes, generatedAt } = await mfa.generateBackupCodes(userId);
114
+ // codes: ['ABCD-1234', 'EFGH-5678', ...]
115
+
116
+ // Check remaining codes
117
+ const remaining = await mfa.getRemainingBackupCodes(userId);
118
+
119
+ // Verify backup code during login
120
+ const result = await mfa.verifyChallenge(userId, 'ABCD-1234', 'backup_codes');
121
+ // Code is marked as used after successful verification
122
+ ```
123
+
124
+ ### User MFA Status
125
+
126
+ ```typescript
127
+ // Check if MFA is enabled
128
+ const enabled = await mfa.isMFAEnabled(userId);
129
+
130
+ // Get enabled methods
131
+ const methods = await mfa.getEnabledMethods(userId);
132
+ // ['totp', 'backup_codes']
133
+
134
+ // Get full MFA status
135
+ const userMFA = await mfa.getUserMFA(userId);
136
+ ```
137
+
138
+ ### Disable MFA
139
+
140
+ ```typescript
141
+ // Disable TOTP (requires valid code)
142
+ await mfa.disableTOTP(userId, '123456');
143
+
144
+ // Disable all MFA methods
145
+ await mfa.disableAllMFA(userId);
146
+ ```
147
+
148
+ ## Configuration
149
+
150
+ ```typescript
151
+ interface MFAConfig {
152
+ issuer: string; // App name shown in authenticator (default: 'Servcraft')
153
+ totpWindow: number; // Time window tolerance (default: 1)
154
+ backupCodesCount: number; // Number of backup codes (default: 10)
155
+ }
156
+ ```
157
+
158
+ ## Security Features
159
+
160
+ ### Account Lockout
161
+
162
+ After 5 failed verification attempts, the account is locked for 15 minutes:
163
+
164
+ ```typescript
165
+ const result = await mfa.verifyChallenge(userId, wrongCode);
166
+ // result: {
167
+ // success: false,
168
+ // remainingAttempts: 2,
169
+ // lockedUntil: undefined
170
+ // }
171
+
172
+ // After 5 failures:
173
+ // result: {
174
+ // success: false,
175
+ // remainingAttempts: 0,
176
+ // lockedUntil: Date (now + 15 minutes)
177
+ // }
178
+ ```
179
+
180
+ ### Challenge Expiration
181
+
182
+ Challenges expire after 5 minutes:
183
+
184
+ ```typescript
185
+ const challenge = await mfa.createChallenge(userId, 'sms');
186
+ // challenge.expiresAt = now + 5 minutes
187
+
188
+ // After expiration, verification fails
189
+ const result = await mfa.verifyChallenge(userId, code, 'sms', challenge.id);
190
+ // result.success = false
191
+ ```
192
+
193
+ ## API Response Types
194
+
195
+ ```typescript
196
+ interface TOTPSetup {
197
+ secret: string; // Raw TOTP secret
198
+ qrCode: string; // QR code as data URL
199
+ manualEntry: string; // Formatted secret for manual entry
200
+ uri: string; // otpauth:// URI
201
+ }
202
+
203
+ interface MFAChallenge {
204
+ id: string;
205
+ userId: string;
206
+ method: MFAMethod;
207
+ expiresAt: Date;
208
+ attempts: number;
209
+ maxAttempts: number;
210
+ verified: boolean;
211
+ }
212
+
213
+ interface MFAVerifyResult {
214
+ success: boolean;
215
+ method: MFAMethod;
216
+ remainingAttempts?: number;
217
+ lockedUntil?: Date;
218
+ }
219
+
220
+ type MFAMethod = 'totp' | 'sms' | 'email' | 'backup_codes';
221
+ ```
222
+
223
+ ## Redis Key Structure
224
+
225
+ | Key Pattern | Purpose | TTL |
226
+ |-------------|---------|-----|
227
+ | `mfa:challenge:{id}` | Active verification challenge | 5 min |
228
+ | `mfa:attempts:{userId}` | Failed attempt counter | 15 min |
229
+
230
+ ## Integration with Auth Flow
231
+
232
+ ```typescript
233
+ // Login flow with MFA
234
+ async function login(email: string, password: string, mfaCode?: string) {
235
+ // 1. Verify credentials
236
+ const user = await authService.verifyCredentials(email, password);
237
+
238
+ // 2. Check if MFA is required
239
+ if (await mfa.isMFAEnabled(user.id)) {
240
+ if (!mfaCode) {
241
+ const methods = await mfa.getEnabledMethods(user.id);
242
+ return { requiresMFA: true, methods };
243
+ }
244
+
245
+ // 3. Verify MFA code
246
+ const result = await mfa.verifyChallenge(user.id, mfaCode);
247
+ if (!result.success) {
248
+ throw new UnauthorizedError('Invalid MFA code', {
249
+ remainingAttempts: result.remainingAttempts,
250
+ lockedUntil: result.lockedUntil,
251
+ });
252
+ }
253
+ }
254
+
255
+ // 4. Issue tokens
256
+ return authService.generateTokens(user);
257
+ }
258
+ ```
259
+
260
+ ## Best Practices
261
+
262
+ 1. **Always offer backup codes** - Users can lose access to their phone
263
+ 2. **Display codes only once** - Show backup codes only when generated
264
+ 3. **Log MFA events** - Track setup, verification, and failures for audit
265
+ 4. **Rate limit challenges** - Prevent challenge spam with rate limiting
266
+ 5. **Secure TOTP secrets** - Encrypt at rest in database