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,327 @@
1
+ # Security Module
2
+
3
+ Comprehensive security features for protecting your application against common web vulnerabilities.
4
+
5
+ ## Features
6
+
7
+ - **Input Sanitization** - XSS prevention with HTML escaping and script stripping
8
+ - **CSRF Protection** - Token-based Cross-Site Request Forgery protection
9
+ - **Security Headers** - Essential HTTP security headers
10
+ - **HPP Protection** - HTTP Parameter Pollution prevention
11
+ - **Suspicious Activity Detection** - Pattern-based attack detection
12
+ - **Security Audit Logging** - Comprehensive security event tracking
13
+
14
+ ## Installation
15
+
16
+ The security module is included in ServCraft by default.
17
+
18
+ ```typescript
19
+ import {
20
+ registerSecurityMiddlewares,
21
+ sanitizeString,
22
+ sanitizeObject,
23
+ getSecurityAuditService,
24
+ } from 'servcraft/modules/security';
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### Register All Security Middlewares
30
+
31
+ ```typescript
32
+ import Fastify from 'fastify';
33
+ import { registerSecurityMiddlewares } from 'servcraft/modules/security';
34
+
35
+ const app = Fastify();
36
+
37
+ await registerSecurityMiddlewares(app, {
38
+ sanitize: true, // Input sanitization
39
+ csrf: false, // CSRF (disabled for API-first apps)
40
+ hpp: true, // HTTP Parameter Pollution protection
41
+ headers: true, // Security headers
42
+ sizeLimit: 10 * 1024 * 1024, // 10MB request size limit
43
+ suspicionDetection: true, // Suspicious activity detection
44
+ });
45
+ ```
46
+
47
+ ## Input Sanitization
48
+
49
+ ### Basic String Sanitization
50
+
51
+ ```typescript
52
+ import { sanitizeString, escapeHtml, stripDangerousHtml } from 'servcraft/modules/security';
53
+
54
+ // Escape HTML entities
55
+ const safe = escapeHtml('<script>alert("xss")</script>');
56
+ // Result: '&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;'
57
+
58
+ // Strip dangerous HTML
59
+ const stripped = stripDangerousHtml('<div onclick="evil()">Safe text</div>');
60
+ // Result: '<div>Safe text</div>'
61
+
62
+ // Full sanitization
63
+ const sanitized = sanitizeString(userInput, {
64
+ escapeHtmlChars: true, // Escape HTML entities
65
+ stripScripts: true, // Remove script tags and event handlers
66
+ trim: true, // Trim whitespace
67
+ maxLength: 1000, // Limit string length
68
+ });
69
+ ```
70
+
71
+ ### Object Sanitization
72
+
73
+ ```typescript
74
+ import { sanitizeObject } from 'servcraft/modules/security';
75
+
76
+ const userInput = {
77
+ name: '<script>alert("xss")</script>',
78
+ email: 'user@example.com',
79
+ bio: 'Hello <img onerror="evil()" src="x">',
80
+ };
81
+
82
+ const safe = sanitizeObject(userInput, {
83
+ escapeHtmlChars: true,
84
+ stripScripts: true,
85
+ });
86
+ // All string values are sanitized recursively
87
+ // Prototype pollution keys (__proto__, constructor) are removed
88
+ ```
89
+
90
+ ### URL Sanitization
91
+
92
+ ```typescript
93
+ import { sanitizeUrl } from 'servcraft/modules/security';
94
+
95
+ sanitizeUrl('https://example.com'); // Returns: 'https://example.com'
96
+ sanitizeUrl('javascript:alert(1)'); // Returns: ''
97
+ sanitizeUrl('data:text/html,...'); // Returns: ''
98
+ ```
99
+
100
+ ### Filename Sanitization
101
+
102
+ ```typescript
103
+ import { sanitizeFilename } from 'servcraft/modules/security';
104
+
105
+ sanitizeFilename('../../../etc/passwd'); // Returns: '______etc_passwd'
106
+ sanitizeFilename('my file (1).pdf'); // Returns: 'my_file__1_.pdf'
107
+ ```
108
+
109
+ ## CSRF Protection
110
+
111
+ ### Setup
112
+
113
+ ```typescript
114
+ import { csrfProtection, generateCsrfToken } from 'servcraft/modules/security';
115
+
116
+ // Generate token for a session
117
+ app.get('/csrf-token', async (request, reply) => {
118
+ const sessionId = request.headers['x-session-id'] || request.ip;
119
+ const token = generateCsrfToken(sessionId);
120
+ return { csrfToken: token };
121
+ });
122
+
123
+ // Protect routes (skip for API with JWT)
124
+ app.addHook('preHandler', csrfProtection());
125
+ ```
126
+
127
+ ### Client Usage
128
+
129
+ ```javascript
130
+ // Get CSRF token
131
+ const { csrfToken } = await fetch('/csrf-token').then(r => r.json());
132
+
133
+ // Include in subsequent requests
134
+ fetch('/api/action', {
135
+ method: 'POST',
136
+ headers: {
137
+ 'Content-Type': 'application/json',
138
+ 'X-CSRF-Token': csrfToken,
139
+ },
140
+ body: JSON.stringify(data),
141
+ });
142
+ ```
143
+
144
+ ## Security Headers
145
+
146
+ The following headers are automatically added:
147
+
148
+ | Header | Value | Purpose |
149
+ |--------|-------|---------|
150
+ | `X-Content-Type-Options` | `nosniff` | Prevent MIME sniffing |
151
+ | `X-Frame-Options` | `DENY` | Prevent clickjacking |
152
+ | `X-XSS-Protection` | `1; mode=block` | XSS filter |
153
+ | `Referrer-Policy` | `strict-origin-when-cross-origin` | Control referrer info |
154
+ | `Permissions-Policy` | `camera=(), microphone=()...` | Disable dangerous features |
155
+ | `Cache-Control` | `no-store` | Prevent caching sensitive data |
156
+
157
+ ## HTTP Parameter Pollution Protection
158
+
159
+ ```typescript
160
+ import { hppProtection } from 'servcraft/modules/security';
161
+
162
+ // Prevent array injection
163
+ // ?user=admin&user=hacker → user=hacker (takes last value)
164
+ app.addHook('preHandler', hppProtection());
165
+
166
+ // Allow specific parameters to be arrays
167
+ app.addHook('preHandler', hppProtection(['tags', 'categories']));
168
+ // ?tags=a&tags=b → tags=['a', 'b'] (allowed)
169
+ ```
170
+
171
+ ## Suspicious Activity Detection
172
+
173
+ ```typescript
174
+ import { suspiciousActivityDetection } from 'servcraft/modules/security';
175
+
176
+ // Log suspicious patterns (default)
177
+ app.addHook('preHandler', suspiciousActivityDetection());
178
+
179
+ // Block suspicious requests
180
+ app.addHook('preHandler', suspiciousActivityDetection({
181
+ blockSuspicious: true,
182
+ }));
183
+ ```
184
+
185
+ ### Detected Patterns
186
+
187
+ - Path traversal: `../`
188
+ - Script injection: `<script`
189
+ - SQL injection: `UNION SELECT`, `DROP TABLE`
190
+ - Template injection: `${}`, `{{}}`
191
+ - Code execution: `exec()`, `eval()`
192
+
193
+ ## Security Audit Service
194
+
195
+ ### Logging Security Events
196
+
197
+ ```typescript
198
+ import { getSecurityAuditService } from 'servcraft/modules/security';
199
+
200
+ const audit = getSecurityAuditService();
201
+
202
+ // Log login success
203
+ await audit.logLoginSuccess(userId, request);
204
+
205
+ // Log login failure
206
+ await audit.logLoginFailed(email, 'Invalid password', request);
207
+
208
+ // Log suspicious activity
209
+ await audit.logSuspiciousActivity('Multiple failed login attempts', request, {
210
+ attempts: 5,
211
+ targetEmail: 'user@example.com',
212
+ });
213
+
214
+ // Log rate limit exceeded
215
+ await audit.logRateLimitExceeded(request, 100);
216
+
217
+ // Log brute force detection
218
+ await audit.logBruteForceDetected(request, 10, '/api/login');
219
+
220
+ // Log access denied
221
+ await audit.logAccessDenied(userId, 'admin-panel', 'view', request);
222
+
223
+ // Log admin action
224
+ await audit.logAdminAction(adminId, 'delete_user', 'users', {
225
+ targetUserId: deletedUserId,
226
+ }, request);
227
+ ```
228
+
229
+ ### Custom Event Logging
230
+
231
+ ```typescript
232
+ await audit.log({
233
+ type: 'auth.mfa.enabled',
234
+ userId: user.id,
235
+ request,
236
+ details: { method: 'totp' },
237
+ success: true,
238
+ });
239
+ ```
240
+
241
+ ### Event Types
242
+
243
+ | Type | Severity | Description |
244
+ |------|----------|-------------|
245
+ | `auth.login.success` | low | Successful login |
246
+ | `auth.login.failed` | medium | Failed login attempt |
247
+ | `auth.mfa.enabled` | medium | MFA enabled |
248
+ | `auth.mfa.disabled` | high | MFA disabled |
249
+ | `suspicious.activity` | high | Suspicious pattern detected |
250
+ | `csrf.violation` | high | CSRF token mismatch |
251
+ | `xss.attempt` | critical | XSS attack detected |
252
+ | `sqli.attempt` | critical | SQL injection detected |
253
+ | `brute.force.detected` | high | Brute force attack |
254
+ | `rate.limit.exceeded` | medium | Rate limit hit |
255
+ | `data.deletion` | critical | Data deletion |
256
+ | `admin.action` | high | Admin action performed |
257
+
258
+ ### Retrieving Security Data
259
+
260
+ ```typescript
261
+ const audit = getSecurityAuditService();
262
+
263
+ // Get recent high/critical alerts
264
+ const alerts = await audit.getRecentAlerts(50);
265
+
266
+ // Get security events for a user
267
+ const userEvents = await audit.getUserEvents(userId, 100);
268
+
269
+ // Get security statistics
270
+ const stats = await audit.getStats(24); // Last 24 hours
271
+ // {
272
+ // totalEvents: 1250,
273
+ // failedLogins: 45,
274
+ // suspiciousActivities: 12,
275
+ // rateLimitExceeded: 230,
276
+ // criticalAlerts: 3,
277
+ // }
278
+ ```
279
+
280
+ ## Storage
281
+
282
+ Security audit events are stored in:
283
+
284
+ 1. **Redis** (real-time, 24h TTL) - For monitoring dashboards
285
+ 2. **PostgreSQL** (long-term) - For compliance and audit trails
286
+
287
+ ### Redis Keys
288
+
289
+ | Key Pattern | TTL | Purpose |
290
+ |-------------|-----|---------|
291
+ | `security:audit:{id}` | 24h | Individual events |
292
+ | `security:alerts:recent` | - | Recent high/critical alerts list |
293
+
294
+ ## Best Practices
295
+
296
+ 1. **Always sanitize user input** before storing or displaying
297
+ 2. **Enable CSRF protection** for browser-based apps
298
+ 3. **Monitor security events** regularly
299
+ 4. **Set up alerts** for critical severity events
300
+ 5. **Review audit logs** for compliance requirements
301
+ 6. **Use rate limiting** in conjunction with brute force detection
302
+
303
+ ## Integration with Other Modules
304
+
305
+ ```typescript
306
+ // With Rate Limiting
307
+ import { createRateLimiter } from 'servcraft/modules/rate-limit';
308
+
309
+ const loginLimiter = createRateLimiter({
310
+ windowMs: 15 * 60 * 1000, // 15 minutes
311
+ max: 5,
312
+ onLimitReached: async (request) => {
313
+ await audit.logBruteForceDetected(request, 5, '/api/login');
314
+ },
315
+ });
316
+
317
+ // With Auth Module
318
+ import { authService } from 'servcraft/modules/auth';
319
+
320
+ authService.on('login:success', async (user, request) => {
321
+ await audit.logLoginSuccess(user.id, request);
322
+ });
323
+
324
+ authService.on('login:failed', async (email, reason, request) => {
325
+ await audit.logLoginFailed(email, reason, request);
326
+ });
327
+ ```
@@ -0,0 +1,382 @@
1
+ # Session Module
2
+
3
+ Server-side session management with Redis storage and optional PostgreSQL persistence.
4
+
5
+ ## Features
6
+
7
+ - **Redis-based Storage** - Fast session access with configurable TTL
8
+ - **Sliding Expiration** - Extend session on activity
9
+ - **Multi-device Support** - List and manage user sessions across devices
10
+ - **Optional Persistence** - PostgreSQL backup for audit trails
11
+ - **Session Metadata** - Store device info, IP, user agent
12
+
13
+ ## Installation
14
+
15
+ ```typescript
16
+ import {
17
+ SessionService,
18
+ createSessionService,
19
+ getSessionService,
20
+ } from 'servcraft/modules/session';
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { getSessionService } from 'servcraft/modules/session';
27
+
28
+ const sessionService = getSessionService();
29
+
30
+ // Create a session
31
+ const session = await sessionService.create({
32
+ userId: 'user-123',
33
+ metadata: {
34
+ device: 'Chrome on Windows',
35
+ ip: request.ip,
36
+ userAgent: request.headers['user-agent'],
37
+ },
38
+ });
39
+
40
+ // Get session
41
+ const currentSession = await sessionService.get(session.id);
42
+
43
+ // Extend session (sliding expiration)
44
+ await sessionService.touch(session.id);
45
+
46
+ // Destroy session
47
+ await sessionService.destroy(session.id);
48
+ ```
49
+
50
+ ## Configuration
51
+
52
+ ```typescript
53
+ import { createSessionService } from 'servcraft/modules/session';
54
+
55
+ const sessionService = createSessionService({
56
+ // Session TTL in seconds (default: 24 hours)
57
+ ttl: 86400,
58
+
59
+ // Enable sliding expiration (default: true)
60
+ slidingExpiration: true,
61
+
62
+ // Redis key prefix (default: 'session:')
63
+ prefix: 'session:',
64
+
65
+ // Enable PostgreSQL persistence (default: false)
66
+ persistToDatabase: true,
67
+ });
68
+ ```
69
+
70
+ ## API Reference
71
+
72
+ ### Create Session
73
+
74
+ ```typescript
75
+ const session = await sessionService.create({
76
+ userId: 'user-123',
77
+ metadata: {
78
+ device: 'Mobile Safari on iOS',
79
+ ip: '192.168.1.1',
80
+ userAgent: 'Mozilla/5.0...',
81
+ location: 'Paris, France',
82
+ },
83
+ });
84
+
85
+ // Returns:
86
+ // {
87
+ // id: 'sess_abc123...',
88
+ // userId: 'user-123',
89
+ // metadata: { ... },
90
+ // createdAt: Date,
91
+ // expiresAt: Date,
92
+ // lastAccessedAt: Date,
93
+ // }
94
+ ```
95
+
96
+ ### Get Session
97
+
98
+ ```typescript
99
+ const session = await sessionService.get('sess_abc123');
100
+
101
+ if (!session) {
102
+ // Session expired or doesn't exist
103
+ throw new Error('Invalid session');
104
+ }
105
+ ```
106
+
107
+ ### Touch Session (Extend TTL)
108
+
109
+ ```typescript
110
+ // Extend session TTL on user activity
111
+ const extended = await sessionService.touch('sess_abc123');
112
+
113
+ if (extended) {
114
+ // Session TTL was reset
115
+ }
116
+ ```
117
+
118
+ ### Destroy Session
119
+
120
+ ```typescript
121
+ // Single session (logout)
122
+ await sessionService.destroy('sess_abc123');
123
+
124
+ // All user sessions (logout everywhere)
125
+ await sessionService.destroyUserSessions('user-123');
126
+ ```
127
+
128
+ ### List User Sessions
129
+
130
+ ```typescript
131
+ const sessions = await sessionService.getUserSessions('user-123');
132
+
133
+ // Returns array of active sessions:
134
+ // [
135
+ // { id: 'sess_abc', device: 'Chrome', lastAccessedAt: Date, current: true },
136
+ // { id: 'sess_xyz', device: 'Mobile', lastAccessedAt: Date, current: false },
137
+ // ]
138
+ ```
139
+
140
+ ### Session Statistics
141
+
142
+ ```typescript
143
+ const stats = await sessionService.getStats();
144
+ // {
145
+ // totalSessions: 1523,
146
+ // activeSessions: 342,
147
+ // expiredSessions: 1181,
148
+ // }
149
+ ```
150
+
151
+ ## Middleware Integration
152
+
153
+ ### Fastify Session Middleware
154
+
155
+ ```typescript
156
+ import { getSessionService } from 'servcraft/modules/session';
157
+
158
+ const sessionService = getSessionService();
159
+
160
+ // Session validation middleware
161
+ app.addHook('preHandler', async (request, reply) => {
162
+ const sessionId = request.cookies.sessionId;
163
+
164
+ if (!sessionId) {
165
+ return reply.status(401).send({ error: 'No session' });
166
+ }
167
+
168
+ const session = await sessionService.get(sessionId);
169
+
170
+ if (!session) {
171
+ return reply.status(401).send({ error: 'Invalid session' });
172
+ }
173
+
174
+ // Extend session on activity
175
+ await sessionService.touch(sessionId);
176
+
177
+ // Attach to request
178
+ request.session = session;
179
+ request.userId = session.userId;
180
+ });
181
+ ```
182
+
183
+ ### Login Handler
184
+
185
+ ```typescript
186
+ app.post('/login', async (request, reply) => {
187
+ const { email, password } = request.body;
188
+
189
+ // Validate credentials
190
+ const user = await authService.validateCredentials(email, password);
191
+
192
+ if (!user) {
193
+ return reply.status(401).send({ error: 'Invalid credentials' });
194
+ }
195
+
196
+ // Create session
197
+ const session = await sessionService.create({
198
+ userId: user.id,
199
+ metadata: {
200
+ ip: request.ip,
201
+ userAgent: request.headers['user-agent'],
202
+ loginMethod: 'password',
203
+ },
204
+ });
205
+
206
+ // Set session cookie
207
+ reply.setCookie('sessionId', session.id, {
208
+ httpOnly: true,
209
+ secure: process.env.NODE_ENV === 'production',
210
+ sameSite: 'strict',
211
+ maxAge: 86400, // 24 hours
212
+ });
213
+
214
+ return { user, sessionId: session.id };
215
+ });
216
+ ```
217
+
218
+ ### Logout Handler
219
+
220
+ ```typescript
221
+ // Logout current session
222
+ app.post('/logout', async (request, reply) => {
223
+ const sessionId = request.cookies.sessionId;
224
+
225
+ if (sessionId) {
226
+ await sessionService.destroy(sessionId);
227
+ }
228
+
229
+ reply.clearCookie('sessionId');
230
+ return { success: true };
231
+ });
232
+
233
+ // Logout all sessions
234
+ app.post('/logout-all', async (request, reply) => {
235
+ await sessionService.destroyUserSessions(request.userId);
236
+
237
+ reply.clearCookie('sessionId');
238
+ return { success: true };
239
+ });
240
+ ```
241
+
242
+ ## Session Management UI
243
+
244
+ ### List Active Sessions
245
+
246
+ ```typescript
247
+ app.get('/sessions', async (request, reply) => {
248
+ const sessions = await sessionService.getUserSessions(request.userId);
249
+
250
+ return sessions.map(session => ({
251
+ id: session.id,
252
+ device: session.metadata?.device || 'Unknown',
253
+ ip: session.metadata?.ip,
254
+ location: session.metadata?.location,
255
+ lastActive: session.lastAccessedAt,
256
+ current: session.id === request.cookies.sessionId,
257
+ }));
258
+ });
259
+ ```
260
+
261
+ ### Revoke Specific Session
262
+
263
+ ```typescript
264
+ app.delete('/sessions/:sessionId', async (request, reply) => {
265
+ const { sessionId } = request.params;
266
+
267
+ // Verify session belongs to user
268
+ const session = await sessionService.get(sessionId);
269
+
270
+ if (!session || session.userId !== request.userId) {
271
+ return reply.status(404).send({ error: 'Session not found' });
272
+ }
273
+
274
+ await sessionService.destroy(sessionId);
275
+ return { success: true };
276
+ });
277
+ ```
278
+
279
+ ## Storage Architecture
280
+
281
+ ### Redis (Primary)
282
+
283
+ Sessions are stored in Redis for fast access:
284
+
285
+ ```
286
+ session:{sessionId} -> {
287
+ id: string,
288
+ userId: string,
289
+ metadata: object,
290
+ createdAt: timestamp,
291
+ expiresAt: timestamp,
292
+ lastAccessedAt: timestamp,
293
+ }
294
+ ```
295
+
296
+ TTL is managed automatically. Sessions expire and are removed by Redis.
297
+
298
+ ### PostgreSQL (Optional)
299
+
300
+ When `persistToDatabase: true`, sessions are also stored in PostgreSQL:
301
+
302
+ ```sql
303
+ CREATE TABLE sessions (
304
+ id VARCHAR(255) PRIMARY KEY,
305
+ user_id VARCHAR(255) NOT NULL,
306
+ metadata JSONB,
307
+ created_at TIMESTAMP DEFAULT NOW(),
308
+ expires_at TIMESTAMP NOT NULL,
309
+ last_accessed_at TIMESTAMP,
310
+ FOREIGN KEY (user_id) REFERENCES users(id)
311
+ );
312
+ ```
313
+
314
+ This provides:
315
+ - Audit trail of all sessions
316
+ - Survival across Redis restarts
317
+ - Analytics on session patterns
318
+
319
+ ## Security Considerations
320
+
321
+ 1. **Use secure cookies** - `httpOnly`, `secure`, `sameSite`
322
+ 2. **Rotate session IDs** after sensitive operations
323
+ 3. **Limit concurrent sessions** per user if needed
324
+ 4. **Log session events** for security auditing
325
+ 5. **Implement idle timeout** in addition to absolute timeout
326
+
327
+ ### Session Rotation
328
+
329
+ ```typescript
330
+ app.post('/change-password', async (request, reply) => {
331
+ // Change password logic...
332
+
333
+ // Destroy all other sessions
334
+ const currentSessionId = request.cookies.sessionId;
335
+ const sessions = await sessionService.getUserSessions(request.userId);
336
+
337
+ for (const session of sessions) {
338
+ if (session.id !== currentSessionId) {
339
+ await sessionService.destroy(session.id);
340
+ }
341
+ }
342
+
343
+ return { success: true };
344
+ });
345
+ ```
346
+
347
+ ## Integration with Auth Module
348
+
349
+ ```typescript
350
+ import { authService } from 'servcraft/modules/auth';
351
+ import { getSessionService } from 'servcraft/modules/session';
352
+
353
+ const sessionService = getSessionService();
354
+
355
+ // On successful login
356
+ authService.on('login:success', async (user, request) => {
357
+ const session = await sessionService.create({
358
+ userId: user.id,
359
+ metadata: {
360
+ ip: request.ip,
361
+ userAgent: request.headers['user-agent'],
362
+ },
363
+ });
364
+ return session;
365
+ });
366
+
367
+ // On logout
368
+ authService.on('logout', async (sessionId) => {
369
+ await sessionService.destroy(sessionId);
370
+ });
371
+
372
+ // On password change
373
+ authService.on('password:changed', async (userId, currentSessionId) => {
374
+ // Destroy all other sessions
375
+ const sessions = await sessionService.getUserSessions(userId);
376
+ for (const session of sessions) {
377
+ if (session.id !== currentSessionId) {
378
+ await sessionService.destroy(session.id);
379
+ }
380
+ }
381
+ });
382
+ ```