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,339 @@
1
+ # Rate Limit Module
2
+
3
+ Flexible rate limiting with multiple algorithms and storage backends.
4
+
5
+ ## Features
6
+
7
+ - **Multiple Algorithms** - Fixed window, sliding window, token bucket
8
+ - **Multiple Stores** - Memory (default), Redis (distributed)
9
+ - **Whitelist/Blacklist** - IP-based access control
10
+ - **Custom Key Generation** - Rate limit by IP, user ID, API key, etc.
11
+ - **Standard Headers** - X-RateLimit-* headers support
12
+ - **Custom Limits** - Per-endpoint or per-user limits
13
+
14
+ ## Algorithms
15
+
16
+ ### Fixed Window
17
+
18
+ Simple counter that resets at fixed intervals. Can allow bursts at window boundaries.
19
+
20
+ ```
21
+ Window 1 Window 2
22
+ [====|====] [====|====]
23
+ ^burst allowed here
24
+ ```
25
+
26
+ ### Sliding Window
27
+
28
+ Weighted count based on position within the window. Prevents boundary bursts.
29
+
30
+ ```
31
+ ◄─────── window ───────►
32
+ Past │ ████░░░░ │ ████████ │ Future
33
+ │ 30% │ 70% │
34
+ Weighted count = (old * 0.3) + new
35
+ ```
36
+
37
+ ### Token Bucket
38
+
39
+ Tokens refill over time, allowing small bursts but enforcing average rate.
40
+
41
+ ```
42
+ Bucket (max 100 tokens)
43
+ ┌──────────────────────────┐
44
+ │ ████████████░░░░░░░░░░░░ │ 60 tokens available
45
+ └──────────────────────────┘
46
+ Refill: 10 tokens/second
47
+ Request consumes 1 token
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ### Basic Setup
53
+
54
+ ```typescript
55
+ import { RateLimitService } from 'servcraft/modules/rate-limit';
56
+ import { RedisStore } from 'servcraft/modules/rate-limit/stores/redis.store';
57
+
58
+ // Simple rate limiter (100 requests per minute)
59
+ const rateLimiter = new RateLimitService({
60
+ max: 100,
61
+ windowMs: 60 * 1000,
62
+ });
63
+
64
+ // Check if request is allowed
65
+ const result = await rateLimiter.check(clientIp);
66
+ if (!result.allowed) {
67
+ // Return 429 Too Many Requests
68
+ }
69
+ ```
70
+
71
+ ### With Redis Store (Distributed)
72
+
73
+ ```typescript
74
+ import { RedisStore } from 'servcraft/modules/rate-limit/stores/redis.store';
75
+
76
+ const redisStore = new RedisStore({
77
+ prefix: 'ratelimit:api:',
78
+ });
79
+
80
+ const rateLimiter = new RateLimitService({
81
+ max: 100,
82
+ windowMs: 60 * 1000,
83
+ algorithm: 'sliding-window',
84
+ }, redisStore);
85
+ ```
86
+
87
+ ### Algorithm Selection
88
+
89
+ ```typescript
90
+ // Fixed window - simplest, good for non-critical APIs
91
+ const fixed = new RateLimitService({
92
+ max: 100,
93
+ windowMs: 60000,
94
+ algorithm: 'fixed-window',
95
+ });
96
+
97
+ // Sliding window - recommended for most use cases
98
+ const sliding = new RateLimitService({
99
+ max: 100,
100
+ windowMs: 60000,
101
+ algorithm: 'sliding-window',
102
+ });
103
+
104
+ // Token bucket - best for APIs that need to allow bursts
105
+ const tokenBucket = new RateLimitService({
106
+ max: 100,
107
+ windowMs: 60000,
108
+ algorithm: 'token-bucket',
109
+ });
110
+ ```
111
+
112
+ ### Custom Key Generation
113
+
114
+ ```typescript
115
+ // Rate limit by user ID
116
+ const userLimiter = new RateLimitService({
117
+ max: 1000,
118
+ windowMs: 60000,
119
+ keyGenerator: (req) => req.user?.id || req.ip,
120
+ });
121
+
122
+ // Rate limit by API key
123
+ const apiLimiter = new RateLimitService({
124
+ max: 5000,
125
+ windowMs: 60000,
126
+ keyGenerator: (req) => req.headers['x-api-key'] || req.ip,
127
+ });
128
+
129
+ // Rate limit by endpoint + IP
130
+ const endpointLimiter = new RateLimitService({
131
+ max: 10,
132
+ windowMs: 60000,
133
+ keyGenerator: (req) => `${req.method}:${req.url}:${req.ip}`,
134
+ });
135
+ ```
136
+
137
+ ### Whitelist and Blacklist
138
+
139
+ ```typescript
140
+ const limiter = new RateLimitService({
141
+ max: 100,
142
+ windowMs: 60000,
143
+ whitelist: ['127.0.0.1', '10.0.0.1'], // Never rate limited
144
+ blacklist: ['192.168.1.100'], // Always blocked
145
+ });
146
+
147
+ // Dynamic whitelist/blacklist
148
+ limiter.addToWhitelist('10.0.0.2');
149
+ limiter.removeFromWhitelist('10.0.0.1');
150
+ limiter.addToBlacklist('suspicious-ip');
151
+ ```
152
+
153
+ ### Custom Limits per Endpoint
154
+
155
+ ```typescript
156
+ // Different limits for different endpoints
157
+ const result = await limiter.check(clientIp, {
158
+ max: 10, // Override default max
159
+ windowMs: 1000, // Override default window
160
+ });
161
+ ```
162
+
163
+ ### Getting Rate Limit Info
164
+
165
+ ```typescript
166
+ const info = await limiter.getInfo(clientIp);
167
+ // {
168
+ // limit: 100,
169
+ // remaining: 45,
170
+ // resetAt: 1703001234567,
171
+ // count: 55,
172
+ // firstRequest: 1703001200000,
173
+ // lastRequest: 1703001230000
174
+ // }
175
+ ```
176
+
177
+ ### Reset Rate Limit
178
+
179
+ ```typescript
180
+ // Reset for specific key
181
+ await limiter.reset(clientIp);
182
+
183
+ // Clear all rate limit data
184
+ await limiter.clear();
185
+
186
+ // Cleanup expired entries (memory store)
187
+ await limiter.cleanup();
188
+ ```
189
+
190
+ ## Configuration
191
+
192
+ ```typescript
193
+ interface RateLimitConfig {
194
+ max: number; // Maximum requests per window
195
+ windowMs: number; // Window size in milliseconds
196
+ algorithm?: Algorithm; // 'fixed-window' | 'sliding-window' | 'token-bucket'
197
+ whitelist?: string[]; // IPs that bypass rate limiting
198
+ blacklist?: string[]; // IPs that are always blocked
199
+ keyGenerator?: (req) => string; // Custom key generation
200
+ skip?: (req) => boolean; // Skip rate limiting for some requests
201
+ onLimitReached?: (req) => void; // Callback when limit reached
202
+ headers?: boolean; // Include X-RateLimit headers (default: true)
203
+ message?: string; // Error message (default: 'Too many requests')
204
+ statusCode?: number; // HTTP status code (default: 429)
205
+ customLimits?: Record<string, { max: number; windowMs: number }>;
206
+ }
207
+ ```
208
+
209
+ ## Response Types
210
+
211
+ ```typescript
212
+ interface RateLimitResult {
213
+ allowed: boolean; // Whether request is allowed
214
+ limit: number; // Maximum requests per window
215
+ remaining: number; // Remaining requests in window
216
+ resetAt: number; // Timestamp when window resets
217
+ retryAfter?: number; // Seconds until retry (when blocked)
218
+ }
219
+
220
+ interface RateLimitInfo {
221
+ limit: number;
222
+ remaining: number;
223
+ resetAt?: number;
224
+ count: number;
225
+ firstRequest?: number;
226
+ lastRequest?: number;
227
+ }
228
+ ```
229
+
230
+ ## HTTP Headers
231
+
232
+ When `headers: true` (default), include these headers in responses:
233
+
234
+ ```http
235
+ X-RateLimit-Limit: 100
236
+ X-RateLimit-Remaining: 45
237
+ X-RateLimit-Reset: 1703001234
238
+ Retry-After: 30 (only when blocked)
239
+ ```
240
+
241
+ ## Fastify Middleware Example
242
+
243
+ ```typescript
244
+ import { FastifyRequest, FastifyReply } from 'fastify';
245
+
246
+ const rateLimiter = new RateLimitService({
247
+ max: 100,
248
+ windowMs: 60000,
249
+ algorithm: 'sliding-window',
250
+ });
251
+
252
+ async function rateLimitMiddleware(
253
+ request: FastifyRequest,
254
+ reply: FastifyReply
255
+ ) {
256
+ const key = request.ip;
257
+ const result = await rateLimiter.check(key);
258
+
259
+ // Set headers
260
+ reply.header('X-RateLimit-Limit', result.limit);
261
+ reply.header('X-RateLimit-Remaining', result.remaining);
262
+ reply.header('X-RateLimit-Reset', Math.ceil(result.resetAt / 1000));
263
+
264
+ if (!result.allowed) {
265
+ reply.header('Retry-After', result.retryAfter);
266
+ return reply.status(429).send({
267
+ error: 'Too Many Requests',
268
+ retryAfter: result.retryAfter,
269
+ });
270
+ }
271
+ }
272
+
273
+ // Register as preHandler
274
+ fastify.addHook('preHandler', rateLimitMiddleware);
275
+ ```
276
+
277
+ ## Pre-configured Limiters
278
+
279
+ ```typescript
280
+ // Strict limiter for sensitive endpoints (login, password reset)
281
+ const strictLimiter = new RateLimitService({
282
+ max: 5,
283
+ windowMs: 15 * 60 * 1000, // 15 minutes
284
+ algorithm: 'sliding-window',
285
+ });
286
+
287
+ // Standard API limiter
288
+ const standardLimiter = new RateLimitService({
289
+ max: 100,
290
+ windowMs: 60 * 1000,
291
+ algorithm: 'sliding-window',
292
+ });
293
+
294
+ // Relaxed limiter for read-only endpoints
295
+ const relaxedLimiter = new RateLimitService({
296
+ max: 1000,
297
+ windowMs: 60 * 1000,
298
+ algorithm: 'token-bucket',
299
+ });
300
+
301
+ // Auth endpoints (very strict)
302
+ const authLimiter = new RateLimitService({
303
+ max: 10,
304
+ windowMs: 60 * 60 * 1000, // 1 hour
305
+ algorithm: 'fixed-window',
306
+ });
307
+ ```
308
+
309
+ ## Redis Store
310
+
311
+ The Redis store enables distributed rate limiting across multiple server instances.
312
+
313
+ ```typescript
314
+ import { RedisStore } from 'servcraft/modules/rate-limit/stores/redis.store';
315
+
316
+ const redisStore = new RedisStore({
317
+ prefix: 'ratelimit:', // Key prefix
318
+ });
319
+
320
+ // Uses atomic Lua scripts for accuracy
321
+ // Supports sliding-window and token-bucket with Redis
322
+ ```
323
+
324
+ ### Redis Key Structure
325
+
326
+ | Key Pattern | Purpose |
327
+ |-------------|---------|
328
+ | `ratelimit:{key}` | Fixed window counter |
329
+ | `ratelimit:sw:{key}` | Sliding window data |
330
+ | `ratelimit:tb:{key}` | Token bucket data |
331
+
332
+ ## Best Practices
333
+
334
+ 1. **Use sliding-window for APIs** - Prevents burst attacks at boundaries
335
+ 2. **Use token-bucket for webhooks** - Allows legitimate burst traffic
336
+ 3. **Use Redis for distributed systems** - Consistent limits across instances
337
+ 4. **Different limits for different users** - Premium users get higher limits
338
+ 5. **Log rate limit events** - Track abuse patterns
339
+ 6. **Whitelist internal services** - Don't rate limit service-to-service calls
@@ -0,0 +1,288 @@
1
+ # Search Module
2
+
3
+ Full-text search with support for multiple backends (Elasticsearch, Meilisearch, in-memory).
4
+
5
+ ## Features
6
+
7
+ - **Multiple Backends** - Elasticsearch, Meilisearch, or in-memory
8
+ - **Full-text Search** - Text search with relevance scoring
9
+ - **Faceted Search** - Filter and aggregate results
10
+ - **Autocomplete** - Search suggestions
11
+ - **Bulk Indexing** - Efficient batch operations
12
+ - **Similar Documents** - Find related content
13
+
14
+ ## Usage
15
+
16
+ ### Basic Setup
17
+
18
+ ```typescript
19
+ import { SearchService } from 'servcraft/modules/search';
20
+
21
+ // In-memory search (default)
22
+ const searchService = new SearchService();
23
+
24
+ // With Elasticsearch
25
+ const esSearch = new SearchService({
26
+ engine: 'elasticsearch',
27
+ elasticsearch: {
28
+ node: 'http://localhost:9200',
29
+ auth: { username: 'elastic', password: 'password' },
30
+ },
31
+ });
32
+
33
+ // With Meilisearch
34
+ const meiliSearch = new SearchService({
35
+ engine: 'meilisearch',
36
+ meilisearch: {
37
+ host: 'http://localhost:7700',
38
+ apiKey: 'masterKey',
39
+ },
40
+ });
41
+ ```
42
+
43
+ ### Index Management
44
+
45
+ ```typescript
46
+ // Create index
47
+ await searchService.createIndex('products', {
48
+ searchableAttributes: ['name', 'description', 'category'],
49
+ filterableAttributes: ['category', 'price', 'inStock'],
50
+ sortableAttributes: ['price', 'createdAt'],
51
+ });
52
+
53
+ // Delete index
54
+ await searchService.deleteIndex('products');
55
+
56
+ // Update settings
57
+ await searchService.updateSettings('products', {
58
+ searchableAttributes: ['name', 'description', 'tags'],
59
+ });
60
+
61
+ // Get statistics
62
+ const stats = await searchService.getStats('products');
63
+ // { documentCount: 1000, indexSize: '5.2MB', ... }
64
+ ```
65
+
66
+ ### Indexing Documents
67
+
68
+ ```typescript
69
+ // Index single document
70
+ await searchService.indexDocument('products', 'prod-123', {
71
+ id: 'prod-123',
72
+ name: 'Wireless Headphones',
73
+ description: 'High-quality wireless headphones with noise cancellation',
74
+ category: 'Electronics',
75
+ price: 99.99,
76
+ inStock: true,
77
+ tags: ['audio', 'wireless', 'headphones'],
78
+ });
79
+
80
+ // Bulk index
81
+ const products = [
82
+ { id: '1', name: 'Product 1', ... },
83
+ { id: '2', name: 'Product 2', ... },
84
+ ];
85
+
86
+ const result = await searchService.indexDocuments('products', products);
87
+ // { success: 2, failed: 0, errors: [] }
88
+ ```
89
+
90
+ ### Searching
91
+
92
+ ```typescript
93
+ // Simple search
94
+ const results = await searchService.search('products', {
95
+ query: 'wireless headphones',
96
+ limit: 20,
97
+ });
98
+ // {
99
+ // hits: [{ document: {...}, score: 0.95 }, ...],
100
+ // total: 45,
101
+ // took: 12
102
+ // }
103
+
104
+ // With filters
105
+ const filtered = await searchService.search('products', {
106
+ query: 'headphones',
107
+ filters: {
108
+ category: 'Electronics',
109
+ price: { $lt: 100 },
110
+ inStock: true,
111
+ },
112
+ limit: 10,
113
+ offset: 0,
114
+ });
115
+
116
+ // With facets
117
+ const faceted = await searchService.searchWithFacets('products', 'headphones', {
118
+ facets: ['category', 'brand'],
119
+ filters: { inStock: true },
120
+ });
121
+ // {
122
+ // hits: [...],
123
+ // facets: {
124
+ // category: { Electronics: 25, Audio: 15 },
125
+ // brand: { Sony: 10, Bose: 8 }
126
+ // }
127
+ // }
128
+ ```
129
+
130
+ ### Document Operations
131
+
132
+ ```typescript
133
+ // Get document
134
+ const doc = await searchService.getDocument('products', 'prod-123');
135
+
136
+ // Update document
137
+ await searchService.updateDocument('products', 'prod-123', {
138
+ price: 89.99,
139
+ inStock: false,
140
+ });
141
+
142
+ // Delete document
143
+ await searchService.deleteDocument('products', 'prod-123');
144
+ ```
145
+
146
+ ### Autocomplete
147
+
148
+ ```typescript
149
+ const suggestions = await searchService.autocomplete('products', 'wire', 10);
150
+ // {
151
+ // suggestions: ['wireless headphones', 'wireless mouse', 'wireless keyboard'],
152
+ // took: 5
153
+ // }
154
+ ```
155
+
156
+ ### Similar Documents
157
+
158
+ ```typescript
159
+ // Find similar products
160
+ const similar = await searchService.searchSimilar('products', 'prod-123', 5);
161
+ // Returns documents similar to product 'prod-123'
162
+ ```
163
+
164
+ ### Reindexing
165
+
166
+ ```typescript
167
+ // Reindex with transformation
168
+ await searchService.reindex(
169
+ 'products-v1',
170
+ 'products-v2',
171
+ (doc) => ({
172
+ ...doc,
173
+ fullName: `${doc.brand} ${doc.name}`,
174
+ priceRange: doc.price < 50 ? 'budget' : 'premium',
175
+ })
176
+ );
177
+ ```
178
+
179
+ ## Configuration
180
+
181
+ ```typescript
182
+ interface SearchConfig {
183
+ engine?: 'memory' | 'elasticsearch' | 'meilisearch';
184
+ defaultSettings?: IndexSettings;
185
+ elasticsearch?: {
186
+ node: string;
187
+ auth?: { username: string; password: string };
188
+ };
189
+ meilisearch?: {
190
+ host: string;
191
+ apiKey: string;
192
+ };
193
+ }
194
+
195
+ interface IndexSettings {
196
+ searchableAttributes?: string[];
197
+ filterableAttributes?: string[];
198
+ sortableAttributes?: string[];
199
+ distinctAttribute?: string;
200
+ }
201
+
202
+ interface SearchQuery {
203
+ query: string;
204
+ filters?: Record<string, unknown>;
205
+ facets?: string[];
206
+ limit?: number;
207
+ offset?: number;
208
+ sort?: string[];
209
+ }
210
+ ```
211
+
212
+ ## Filter Operators
213
+
214
+ | Operator | Description | Example |
215
+ |----------|-------------|---------|
216
+ | `$eq` | Equal | `{ status: { $eq: 'active' } }` |
217
+ | `$ne` | Not equal | `{ status: { $ne: 'deleted' } }` |
218
+ | `$gt` | Greater than | `{ price: { $gt: 50 } }` |
219
+ | `$gte` | Greater or equal | `{ rating: { $gte: 4 } }` |
220
+ | `$lt` | Less than | `{ price: { $lt: 100 } }` |
221
+ | `$lte` | Less or equal | `{ stock: { $lte: 10 } }` |
222
+ | `$in` | In array | `{ category: { $in: ['A', 'B'] } }` |
223
+ | `$nin` | Not in array | `{ status: { $nin: ['deleted'] } }` |
224
+
225
+ ## Fastify Integration
226
+
227
+ ```typescript
228
+ // Search endpoint
229
+ fastify.get('/api/search', async (request, reply) => {
230
+ const { q, category, page = 1, limit = 20 } = request.query;
231
+
232
+ const results = await searchService.search('products', {
233
+ query: q,
234
+ filters: category ? { category } : undefined,
235
+ limit,
236
+ offset: (page - 1) * limit,
237
+ });
238
+
239
+ return {
240
+ success: true,
241
+ data: results.hits.map(h => h.document),
242
+ total: results.total,
243
+ page,
244
+ totalPages: Math.ceil(results.total / limit),
245
+ };
246
+ });
247
+
248
+ // Autocomplete endpoint
249
+ fastify.get('/api/search/suggest', async (request, reply) => {
250
+ const { q } = request.query;
251
+
252
+ const suggestions = await searchService.autocomplete('products', q, 5);
253
+ return { suggestions: suggestions.suggestions };
254
+ });
255
+ ```
256
+
257
+ ## Indexing Strategy
258
+
259
+ ```typescript
260
+ // When to index
261
+ userService.on('user:created', async (user) => {
262
+ await searchService.indexDocument('users', user.id, {
263
+ id: user.id,
264
+ name: user.name,
265
+ email: user.email,
266
+ });
267
+ });
268
+
269
+ userService.on('user:updated', async (user) => {
270
+ await searchService.updateDocument('users', user.id, {
271
+ name: user.name,
272
+ email: user.email,
273
+ });
274
+ });
275
+
276
+ userService.on('user:deleted', async (userId) => {
277
+ await searchService.deleteDocument('users', userId);
278
+ });
279
+ ```
280
+
281
+ ## Best Practices
282
+
283
+ 1. **Index Design** - Only index searchable/filterable fields
284
+ 2. **Bulk Operations** - Use bulk indexing for large datasets
285
+ 3. **Pagination** - Always paginate search results
286
+ 4. **Relevance Tuning** - Configure searchable attributes by importance
287
+ 5. **Sync Strategy** - Keep search index in sync with database
288
+ 6. **Monitoring** - Track search latency and index size