servcraft 0.1.0 → 0.1.1

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 (216) hide show
  1. package/.claude/settings.local.json +29 -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/README.md +1070 -1
  9. package/dist/cli/index.cjs +2026 -2168
  10. package/dist/cli/index.cjs.map +1 -1
  11. package/dist/cli/index.js +2026 -2168
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/index.cjs +595 -616
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +114 -52
  16. package/dist/index.d.ts +114 -52
  17. package/dist/index.js +595 -616
  18. package/dist/index.js.map +1 -1
  19. package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
  20. package/docs/DATABASE_MULTI_ORM.md +399 -0
  21. package/docs/PHASE1_BREAKDOWN.md +346 -0
  22. package/docs/PROGRESS.md +550 -0
  23. package/docs/modules/ANALYTICS.md +226 -0
  24. package/docs/modules/API-VERSIONING.md +252 -0
  25. package/docs/modules/AUDIT.md +192 -0
  26. package/docs/modules/AUTH.md +431 -0
  27. package/docs/modules/CACHE.md +346 -0
  28. package/docs/modules/EMAIL.md +254 -0
  29. package/docs/modules/FEATURE-FLAG.md +291 -0
  30. package/docs/modules/I18N.md +294 -0
  31. package/docs/modules/MEDIA-PROCESSING.md +281 -0
  32. package/docs/modules/MFA.md +266 -0
  33. package/docs/modules/NOTIFICATION.md +311 -0
  34. package/docs/modules/OAUTH.md +237 -0
  35. package/docs/modules/PAYMENT.md +804 -0
  36. package/docs/modules/QUEUE.md +540 -0
  37. package/docs/modules/RATE-LIMIT.md +339 -0
  38. package/docs/modules/SEARCH.md +288 -0
  39. package/docs/modules/SECURITY.md +327 -0
  40. package/docs/modules/SESSION.md +382 -0
  41. package/docs/modules/SWAGGER.md +305 -0
  42. package/docs/modules/UPLOAD.md +296 -0
  43. package/docs/modules/USER.md +505 -0
  44. package/docs/modules/VALIDATION.md +294 -0
  45. package/docs/modules/WEBHOOK.md +270 -0
  46. package/docs/modules/WEBSOCKET.md +691 -0
  47. package/package.json +53 -38
  48. package/prisma/schema.prisma +395 -1
  49. package/src/cli/commands/add-module.ts +520 -87
  50. package/src/cli/commands/db.ts +3 -4
  51. package/src/cli/commands/docs.ts +256 -6
  52. package/src/cli/commands/generate.ts +12 -19
  53. package/src/cli/commands/init.ts +384 -214
  54. package/src/cli/index.ts +0 -4
  55. package/src/cli/templates/repository.ts +6 -1
  56. package/src/cli/templates/routes.ts +6 -21
  57. package/src/cli/utils/docs-generator.ts +6 -7
  58. package/src/cli/utils/env-manager.ts +717 -0
  59. package/src/cli/utils/field-parser.ts +16 -7
  60. package/src/cli/utils/interactive-prompt.ts +223 -0
  61. package/src/cli/utils/template-manager.ts +346 -0
  62. package/src/config/database.config.ts +183 -0
  63. package/src/config/env.ts +0 -10
  64. package/src/config/index.ts +0 -14
  65. package/src/core/server.ts +1 -1
  66. package/src/database/adapters/mongoose.adapter.ts +132 -0
  67. package/src/database/adapters/prisma.adapter.ts +118 -0
  68. package/src/database/connection.ts +190 -0
  69. package/src/database/interfaces/database.interface.ts +85 -0
  70. package/src/database/interfaces/index.ts +7 -0
  71. package/src/database/interfaces/repository.interface.ts +129 -0
  72. package/src/database/models/mongoose/index.ts +7 -0
  73. package/src/database/models/mongoose/payment.schema.ts +347 -0
  74. package/src/database/models/mongoose/user.schema.ts +154 -0
  75. package/src/database/prisma.ts +1 -4
  76. package/src/database/redis.ts +101 -0
  77. package/src/database/repositories/mongoose/index.ts +7 -0
  78. package/src/database/repositories/mongoose/payment.repository.ts +380 -0
  79. package/src/database/repositories/mongoose/user.repository.ts +255 -0
  80. package/src/database/seed.ts +6 -1
  81. package/src/index.ts +9 -20
  82. package/src/middleware/security.ts +2 -6
  83. package/src/modules/analytics/analytics.routes.ts +80 -0
  84. package/src/modules/analytics/analytics.service.ts +364 -0
  85. package/src/modules/analytics/index.ts +18 -0
  86. package/src/modules/analytics/types.ts +180 -0
  87. package/src/modules/api-versioning/index.ts +15 -0
  88. package/src/modules/api-versioning/types.ts +86 -0
  89. package/src/modules/api-versioning/versioning.middleware.ts +120 -0
  90. package/src/modules/api-versioning/versioning.routes.ts +54 -0
  91. package/src/modules/api-versioning/versioning.service.ts +189 -0
  92. package/src/modules/audit/audit.repository.ts +206 -0
  93. package/src/modules/audit/audit.service.ts +27 -59
  94. package/src/modules/auth/auth.controller.ts +2 -2
  95. package/src/modules/auth/auth.middleware.ts +3 -9
  96. package/src/modules/auth/auth.routes.ts +10 -107
  97. package/src/modules/auth/auth.service.ts +126 -23
  98. package/src/modules/auth/index.ts +3 -4
  99. package/src/modules/cache/cache.service.ts +367 -0
  100. package/src/modules/cache/index.ts +10 -0
  101. package/src/modules/cache/types.ts +44 -0
  102. package/src/modules/email/email.service.ts +3 -10
  103. package/src/modules/email/templates.ts +2 -8
  104. package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
  105. package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
  106. package/src/modules/feature-flag/feature-flag.service.ts +566 -0
  107. package/src/modules/feature-flag/index.ts +20 -0
  108. package/src/modules/feature-flag/types.ts +192 -0
  109. package/src/modules/i18n/i18n.middleware.ts +186 -0
  110. package/src/modules/i18n/i18n.routes.ts +191 -0
  111. package/src/modules/i18n/i18n.service.ts +456 -0
  112. package/src/modules/i18n/index.ts +18 -0
  113. package/src/modules/i18n/types.ts +118 -0
  114. package/src/modules/media-processing/index.ts +17 -0
  115. package/src/modules/media-processing/media-processing.routes.ts +111 -0
  116. package/src/modules/media-processing/media-processing.service.ts +245 -0
  117. package/src/modules/media-processing/types.ts +156 -0
  118. package/src/modules/mfa/index.ts +20 -0
  119. package/src/modules/mfa/mfa.repository.ts +206 -0
  120. package/src/modules/mfa/mfa.routes.ts +595 -0
  121. package/src/modules/mfa/mfa.service.ts +572 -0
  122. package/src/modules/mfa/totp.ts +150 -0
  123. package/src/modules/mfa/types.ts +57 -0
  124. package/src/modules/notification/index.ts +20 -0
  125. package/src/modules/notification/notification.repository.ts +356 -0
  126. package/src/modules/notification/notification.service.ts +483 -0
  127. package/src/modules/notification/types.ts +119 -0
  128. package/src/modules/oauth/index.ts +20 -0
  129. package/src/modules/oauth/oauth.repository.ts +219 -0
  130. package/src/modules/oauth/oauth.routes.ts +446 -0
  131. package/src/modules/oauth/oauth.service.ts +293 -0
  132. package/src/modules/oauth/providers/apple.provider.ts +250 -0
  133. package/src/modules/oauth/providers/facebook.provider.ts +181 -0
  134. package/src/modules/oauth/providers/github.provider.ts +248 -0
  135. package/src/modules/oauth/providers/google.provider.ts +189 -0
  136. package/src/modules/oauth/providers/twitter.provider.ts +214 -0
  137. package/src/modules/oauth/types.ts +94 -0
  138. package/src/modules/payment/index.ts +19 -0
  139. package/src/modules/payment/payment.repository.ts +733 -0
  140. package/src/modules/payment/payment.routes.ts +390 -0
  141. package/src/modules/payment/payment.service.ts +354 -0
  142. package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
  143. package/src/modules/payment/providers/paypal.provider.ts +190 -0
  144. package/src/modules/payment/providers/stripe.provider.ts +215 -0
  145. package/src/modules/payment/types.ts +140 -0
  146. package/src/modules/queue/cron.ts +438 -0
  147. package/src/modules/queue/index.ts +87 -0
  148. package/src/modules/queue/queue.routes.ts +600 -0
  149. package/src/modules/queue/queue.service.ts +842 -0
  150. package/src/modules/queue/types.ts +222 -0
  151. package/src/modules/queue/workers.ts +366 -0
  152. package/src/modules/rate-limit/index.ts +59 -0
  153. package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
  154. package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
  155. package/src/modules/rate-limit/rate-limit.service.ts +348 -0
  156. package/src/modules/rate-limit/stores/memory.store.ts +165 -0
  157. package/src/modules/rate-limit/stores/redis.store.ts +322 -0
  158. package/src/modules/rate-limit/types.ts +153 -0
  159. package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
  160. package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
  161. package/src/modules/search/adapters/memory.adapter.ts +278 -0
  162. package/src/modules/search/index.ts +21 -0
  163. package/src/modules/search/search.service.ts +234 -0
  164. package/src/modules/search/types.ts +214 -0
  165. package/src/modules/security/index.ts +40 -0
  166. package/src/modules/security/sanitize.ts +223 -0
  167. package/src/modules/security/security-audit.service.ts +388 -0
  168. package/src/modules/security/security.middleware.ts +398 -0
  169. package/src/modules/session/index.ts +3 -0
  170. package/src/modules/session/session.repository.ts +159 -0
  171. package/src/modules/session/session.service.ts +340 -0
  172. package/src/modules/session/types.ts +38 -0
  173. package/src/modules/swagger/index.ts +7 -1
  174. package/src/modules/swagger/schema-builder.ts +16 -4
  175. package/src/modules/swagger/swagger.service.ts +9 -10
  176. package/src/modules/swagger/types.ts +0 -2
  177. package/src/modules/upload/index.ts +14 -0
  178. package/src/modules/upload/types.ts +83 -0
  179. package/src/modules/upload/upload.repository.ts +199 -0
  180. package/src/modules/upload/upload.routes.ts +311 -0
  181. package/src/modules/upload/upload.service.ts +448 -0
  182. package/src/modules/user/index.ts +3 -3
  183. package/src/modules/user/user.controller.ts +15 -9
  184. package/src/modules/user/user.repository.ts +237 -113
  185. package/src/modules/user/user.routes.ts +39 -164
  186. package/src/modules/user/user.service.ts +4 -3
  187. package/src/modules/validation/validator.ts +12 -17
  188. package/src/modules/webhook/index.ts +91 -0
  189. package/src/modules/webhook/retry.ts +196 -0
  190. package/src/modules/webhook/signature.ts +135 -0
  191. package/src/modules/webhook/types.ts +181 -0
  192. package/src/modules/webhook/webhook.repository.ts +358 -0
  193. package/src/modules/webhook/webhook.routes.ts +442 -0
  194. package/src/modules/webhook/webhook.service.ts +457 -0
  195. package/src/modules/websocket/features.ts +504 -0
  196. package/src/modules/websocket/index.ts +106 -0
  197. package/src/modules/websocket/middlewares.ts +298 -0
  198. package/src/modules/websocket/types.ts +181 -0
  199. package/src/modules/websocket/websocket.service.ts +692 -0
  200. package/src/utils/errors.ts +7 -0
  201. package/src/utils/pagination.ts +4 -1
  202. package/tests/helpers/db-check.ts +79 -0
  203. package/tests/integration/auth-redis.test.ts +94 -0
  204. package/tests/integration/cache-redis.test.ts +387 -0
  205. package/tests/integration/mongoose-repositories.test.ts +410 -0
  206. package/tests/integration/payment-prisma.test.ts +637 -0
  207. package/tests/integration/queue-bullmq.test.ts +417 -0
  208. package/tests/integration/user-prisma.test.ts +441 -0
  209. package/tests/integration/websocket-socketio.test.ts +552 -0
  210. package/tests/setup.ts +11 -9
  211. package/vitest.config.ts +3 -8
  212. package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
  213. package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
  214. package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
  215. package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
  216. package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
@@ -0,0 +1,294 @@
1
+ # Validation Module
2
+
3
+ Request validation using Zod schemas with type-safe validation helpers.
4
+
5
+ ## Features
6
+
7
+ - **Zod Integration** - Full Zod schema support
8
+ - **Type Safety** - Full TypeScript inference
9
+ - **Error Formatting** - Structured error messages
10
+ - **Common Schemas** - Pre-built validation schemas
11
+ - **Request Validation** - Body, query, and params validation
12
+
13
+ ## Usage
14
+
15
+ ### Basic Validation
16
+
17
+ ```typescript
18
+ import { z } from 'zod';
19
+ import { validate, validateBody, validateQuery, validateParams } from 'servcraft/modules/validation';
20
+
21
+ // Define schema
22
+ const userSchema = z.object({
23
+ email: z.string().email(),
24
+ name: z.string().min(2).max(50),
25
+ age: z.number().min(18).optional(),
26
+ });
27
+
28
+ // Validate data
29
+ const validatedUser = validate(userSchema, requestBody);
30
+ // Returns typed data or throws ValidationError
31
+ ```
32
+
33
+ ### Request Validation
34
+
35
+ ```typescript
36
+ // Validate request body
37
+ const createUser = async (request, reply) => {
38
+ const body = validateBody(userSchema, request.body);
39
+ // body is typed as { email: string; name: string; age?: number }
40
+ };
41
+
42
+ // Validate query parameters
43
+ const listUsers = async (request, reply) => {
44
+ const query = validateQuery(paginationSchema, request.query);
45
+ // query is typed with pagination fields
46
+ };
47
+
48
+ // Validate URL parameters
49
+ const getUser = async (request, reply) => {
50
+ const params = validateParams(idParamSchema, request.params);
51
+ // params is typed as { id: string }
52
+ };
53
+ ```
54
+
55
+ ## Pre-built Schemas
56
+
57
+ ### ID Parameter
58
+
59
+ ```typescript
60
+ import { idParamSchema, IdParam } from 'servcraft/modules/validation';
61
+
62
+ // Schema: { id: string (UUID) }
63
+ const params = validateParams(idParamSchema, request.params);
64
+ ```
65
+
66
+ ### Pagination
67
+
68
+ ```typescript
69
+ import { paginationSchema, PaginationInput } from 'servcraft/modules/validation';
70
+
71
+ // Schema: { page, limit, sortBy, sortOrder }
72
+ const query = validateQuery(paginationSchema, request.query);
73
+ // Defaults: page=1, limit=20, sortOrder='asc'
74
+ ```
75
+
76
+ ### Search
77
+
78
+ ```typescript
79
+ import { searchSchema } from 'servcraft/modules/validation';
80
+
81
+ // Schema: { q?, search? }
82
+ const query = validateQuery(searchSchema, request.query);
83
+ ```
84
+
85
+ ### Email
86
+
87
+ ```typescript
88
+ import { emailSchema } from 'servcraft/modules/validation';
89
+
90
+ const email = emailSchema.parse('user@example.com');
91
+ // Throws if invalid email
92
+ ```
93
+
94
+ ### Password (Strong)
95
+
96
+ ```typescript
97
+ import { passwordSchema } from 'servcraft/modules/validation';
98
+
99
+ // Requirements:
100
+ // - Minimum 8 characters
101
+ // - At least one uppercase letter
102
+ // - At least one lowercase letter
103
+ // - At least one number
104
+ // - At least one special character
105
+
106
+ const password = passwordSchema.parse('MyP@ssw0rd!');
107
+ ```
108
+
109
+ ### URL
110
+
111
+ ```typescript
112
+ import { urlSchema } from 'servcraft/modules/validation';
113
+
114
+ const url = urlSchema.parse('https://example.com');
115
+ ```
116
+
117
+ ### Phone
118
+
119
+ ```typescript
120
+ import { phoneSchema } from 'servcraft/modules/validation';
121
+
122
+ // International format: +1234567890
123
+ const phone = phoneSchema.parse('+12025551234');
124
+ ```
125
+
126
+ ### Date
127
+
128
+ ```typescript
129
+ import { dateSchema, futureDateSchema, pastDateSchema } from 'servcraft/modules/validation';
130
+
131
+ // Any valid date
132
+ const date = dateSchema.parse('2024-12-20');
133
+
134
+ // Must be in the future
135
+ const futureDate = futureDateSchema.parse('2025-01-01');
136
+
137
+ // Must be in the past
138
+ const pastDate = pastDateSchema.parse('2020-01-01');
139
+ ```
140
+
141
+ ## Custom Schemas
142
+
143
+ ```typescript
144
+ import { z } from 'zod';
145
+
146
+ // User registration schema
147
+ const registerSchema = z.object({
148
+ email: z.string().email('Invalid email address'),
149
+ password: z.string()
150
+ .min(8, 'Password must be at least 8 characters')
151
+ .regex(/[A-Z]/, 'Password must contain uppercase'),
152
+ confirmPassword: z.string(),
153
+ name: z.string().min(2).max(50),
154
+ acceptTerms: z.literal(true, {
155
+ errorMap: () => ({ message: 'You must accept the terms' }),
156
+ }),
157
+ }).refine(data => data.password === data.confirmPassword, {
158
+ message: 'Passwords do not match',
159
+ path: ['confirmPassword'],
160
+ });
161
+
162
+ // Order schema
163
+ const orderSchema = z.object({
164
+ items: z.array(z.object({
165
+ productId: z.string().uuid(),
166
+ quantity: z.number().int().positive(),
167
+ })).min(1, 'Order must have at least one item'),
168
+ shippingAddress: z.object({
169
+ street: z.string(),
170
+ city: z.string(),
171
+ zipCode: z.string(),
172
+ country: z.string().length(2),
173
+ }),
174
+ notes: z.string().max(500).optional(),
175
+ });
176
+ ```
177
+
178
+ ## Error Handling
179
+
180
+ ```typescript
181
+ import { ValidationError } from 'servcraft/utils/errors';
182
+
183
+ try {
184
+ const data = validate(schema, input);
185
+ } catch (error) {
186
+ if (error instanceof ValidationError) {
187
+ // error.errors is structured:
188
+ // {
189
+ // email: ['Invalid email address'],
190
+ // password: ['Password must be at least 8 characters'],
191
+ // 'address.zipCode': ['Invalid zip code']
192
+ // }
193
+
194
+ return reply.status(400).send({
195
+ success: false,
196
+ message: 'Validation failed',
197
+ errors: error.errors,
198
+ });
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## Fastify Integration
204
+
205
+ ```typescript
206
+ import { z } from 'zod';
207
+ import { validateBody, validateQuery, validateParams } from 'servcraft/modules/validation';
208
+
209
+ // Route with validation
210
+ fastify.post('/api/users', async (request, reply) => {
211
+ const body = validateBody(registerSchema, request.body);
212
+
213
+ const user = await userService.create(body);
214
+ return { success: true, data: user };
215
+ });
216
+
217
+ // With query and params
218
+ fastify.get('/api/users/:id/orders', async (request, reply) => {
219
+ const params = validateParams(idParamSchema, request.params);
220
+ const query = validateQuery(paginationSchema, request.query);
221
+
222
+ const orders = await orderService.findByUser(params.id, query);
223
+ return { success: true, data: orders };
224
+ });
225
+ ```
226
+
227
+ ## Type Inference
228
+
229
+ ```typescript
230
+ import { z } from 'zod';
231
+
232
+ const schema = z.object({
233
+ name: z.string(),
234
+ age: z.number(),
235
+ });
236
+
237
+ // Infer type from schema
238
+ type User = z.infer<typeof schema>;
239
+ // { name: string; age: number }
240
+
241
+ // Use in function
242
+ function createUser(data: z.infer<typeof schema>) {
243
+ // data is typed
244
+ }
245
+ ```
246
+
247
+ ## Common Patterns
248
+
249
+ ### Optional with Default
250
+
251
+ ```typescript
252
+ const schema = z.object({
253
+ limit: z.number().default(20),
254
+ active: z.boolean().default(true),
255
+ });
256
+ ```
257
+
258
+ ### Transform
259
+
260
+ ```typescript
261
+ const schema = z.object({
262
+ email: z.string().email().toLowerCase(),
263
+ tags: z.string().transform(s => s.split(',')),
264
+ });
265
+ ```
266
+
267
+ ### Refinement
268
+
269
+ ```typescript
270
+ const schema = z.object({
271
+ startDate: z.date(),
272
+ endDate: z.date(),
273
+ }).refine(
274
+ data => data.endDate > data.startDate,
275
+ { message: 'End date must be after start date' }
276
+ );
277
+ ```
278
+
279
+ ### Union Types
280
+
281
+ ```typescript
282
+ const schema = z.discriminatedUnion('type', [
283
+ z.object({ type: z.literal('email'), email: z.string().email() }),
284
+ z.object({ type: z.literal('sms'), phone: z.string() }),
285
+ ]);
286
+ ```
287
+
288
+ ## Best Practices
289
+
290
+ 1. **Reuse Schemas** - Create reusable schemas for common patterns
291
+ 2. **Meaningful Errors** - Provide clear error messages
292
+ 3. **Type Safety** - Use `z.infer<typeof schema>` for types
293
+ 4. **Validation Early** - Validate at the start of handlers
294
+ 5. **Sanitize Input** - Use transforms for normalization
@@ -0,0 +1,270 @@
1
+ # Webhook Module
2
+
3
+ Outbound webhook system with retry logic, signatures, and delivery tracking.
4
+
5
+ ## Features
6
+
7
+ - **Endpoint Management** - Create, update, delete webhook endpoints
8
+ - **Event Publishing** - Publish events to subscribed endpoints
9
+ - **Automatic Retries** - Exponential backoff with configurable limits
10
+ - **Signature Verification** - HMAC signatures for payload integrity
11
+ - **Delivery Tracking** - Full delivery history with status tracking
12
+ - **Secret Rotation** - Rotate endpoint secrets without downtime
13
+
14
+ ## Architecture
15
+
16
+ ```
17
+ ┌──────────────────────────────────────────────────────────────┐
18
+ │ Webhook Service │
19
+ ├──────────────────────────────────────────────────────────────┤
20
+ │ Endpoint Mgmt │ Event Publishing │ Retry Processor │
21
+ └────────┬────────┴─────────┬──────────┴──────────┬────────────┘
22
+ │ │ │
23
+ ▼ ▼ ▼
24
+ ┌──────────────────────────────────────────────────────────────┐
25
+ │ Prisma Repository │
26
+ ├──────────────────────────────────────────────────────────────┤
27
+ │ Endpoints │ Deliveries │ Statistics │
28
+ └─────────────────┴──────────────────┴─────────────────────────┘
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Basic Setup
34
+
35
+ ```typescript
36
+ import { createWebhookService } from 'servcraft/modules/webhook';
37
+
38
+ const webhookService = createWebhookService({
39
+ maxRetries: 5,
40
+ initialRetryDelay: 1000,
41
+ maxRetryDelay: 60000,
42
+ backoffMultiplier: 2,
43
+ timeout: 10000,
44
+ enableSignature: true,
45
+ signatureHeader: 'X-Webhook-Signature',
46
+ timestampHeader: 'X-Webhook-Timestamp',
47
+ });
48
+ ```
49
+
50
+ ### Endpoint Management
51
+
52
+ ```typescript
53
+ // Create endpoint
54
+ const endpoint = await webhookService.createEndpoint({
55
+ url: 'https://example.com/webhooks',
56
+ events: ['user.created', 'user.updated', 'order.completed'],
57
+ headers: { 'X-Custom-Header': 'value' },
58
+ description: 'Main webhook endpoint',
59
+ });
60
+ // endpoint.secret is auto-generated
61
+
62
+ // List endpoints
63
+ const endpoints = await webhookService.listEndpoints();
64
+
65
+ // Update endpoint
66
+ await webhookService.updateEndpoint(endpoint.id, {
67
+ events: ['user.created', 'user.updated'],
68
+ enabled: true,
69
+ });
70
+
71
+ // Rotate secret
72
+ const rotated = await webhookService.rotateSecret(endpoint.id);
73
+ // rotated.secret is new secret
74
+
75
+ // Delete endpoint
76
+ await webhookService.deleteEndpoint(endpoint.id);
77
+ ```
78
+
79
+ ### Publishing Events
80
+
81
+ ```typescript
82
+ // Publish to all matching endpoints
83
+ const event = await webhookService.publishEvent('user.created', {
84
+ userId: 'user-123',
85
+ email: 'user@example.com',
86
+ createdAt: new Date().toISOString(),
87
+ });
88
+
89
+ // Publish to specific endpoints only
90
+ await webhookService.publishEvent('order.completed', payload, ['endpoint-id-1', 'endpoint-id-2']);
91
+ ```
92
+
93
+ ### Delivery Management
94
+
95
+ ```typescript
96
+ // Get delivery status
97
+ const delivery = await webhookService.getDelivery(deliveryId);
98
+ // {
99
+ // id, endpointId, eventType, payload,
100
+ // status: 'pending' | 'success' | 'retrying' | 'failed',
101
+ // attempts, deliveredAt, error
102
+ // }
103
+
104
+ // List deliveries with filters
105
+ const deliveries = await webhookService.listDeliveries({
106
+ endpointId: 'endpoint-123',
107
+ status: 'failed',
108
+ eventType: 'user.created',
109
+ });
110
+
111
+ // Manual retry
112
+ await webhookService.retryDelivery(deliveryId);
113
+
114
+ // Get statistics
115
+ const stats = await webhookService.getStats(endpointId);
116
+ // {
117
+ // totalEvents, successfulDeliveries, failedDeliveries,
118
+ // pendingDeliveries, successRate
119
+ // }
120
+ ```
121
+
122
+ ### Cleanup
123
+
124
+ ```typescript
125
+ // Clean up old deliveries (older than 30 days)
126
+ const deleted = await webhookService.cleanup(30);
127
+
128
+ // Stop retry processor (on shutdown)
129
+ webhookService.stop();
130
+ ```
131
+
132
+ ## Configuration
133
+
134
+ ```typescript
135
+ interface WebhookConfig {
136
+ maxRetries?: number; // Max retry attempts (default: 5)
137
+ initialRetryDelay?: number; // First retry delay ms (default: 1000)
138
+ maxRetryDelay?: number; // Max retry delay ms (default: 60000)
139
+ backoffMultiplier?: number; // Backoff multiplier (default: 2)
140
+ timeout?: number; // Request timeout ms (default: 10000)
141
+ enableSignature?: boolean; // Enable HMAC signatures (default: true)
142
+ signatureHeader?: string; // Signature header name
143
+ timestampHeader?: string; // Timestamp header name
144
+ }
145
+ ```
146
+
147
+ ## Webhook Payload Format
148
+
149
+ Delivered webhooks have this structure:
150
+
151
+ ```json
152
+ {
153
+ "id": "delivery-uuid",
154
+ "type": "user.created",
155
+ "created": "2024-01-15T10:30:00.000Z",
156
+ "data": {
157
+ "userId": "user-123",
158
+ "email": "user@example.com"
159
+ }
160
+ }
161
+ ```
162
+
163
+ ## Signature Verification
164
+
165
+ Webhooks are signed using HMAC-SHA256. Recipients should verify:
166
+
167
+ ```typescript
168
+ // Headers sent with webhook
169
+ // X-Webhook-Signature: t=1705312200,v1=abc123...
170
+ // X-Webhook-Timestamp: 1705312200
171
+
172
+ // Verification (recipient side)
173
+ import crypto from 'crypto';
174
+
175
+ function verifyWebhookSignature(
176
+ payload: string,
177
+ signature: string,
178
+ timestamp: string,
179
+ secret: string
180
+ ): boolean {
181
+ const signedPayload = `${timestamp}.${payload}`;
182
+ const expectedSignature = crypto
183
+ .createHmac('sha256', secret)
184
+ .update(signedPayload)
185
+ .digest('hex');
186
+
187
+ // Parse signature header: t=timestamp,v1=signature
188
+ const parts = signature.split(',');
189
+ const receivedSig = parts.find(p => p.startsWith('v1='))?.slice(3);
190
+
191
+ return crypto.timingSafeEqual(
192
+ Buffer.from(expectedSignature),
193
+ Buffer.from(receivedSig || '')
194
+ );
195
+ }
196
+ ```
197
+
198
+ ## Retry Strategy
199
+
200
+ Retries use exponential backoff:
201
+
202
+ | Attempt | Delay |
203
+ |---------|-------|
204
+ | 1 | Immediate |
205
+ | 2 | 1 second |
206
+ | 3 | 2 seconds |
207
+ | 4 | 4 seconds |
208
+ | 5 | 8 seconds |
209
+ | 6+ | Failed |
210
+
211
+ ## Event Types
212
+
213
+ Define your event types:
214
+
215
+ ```typescript
216
+ type WebhookEventType =
217
+ | 'user.created'
218
+ | 'user.updated'
219
+ | 'user.deleted'
220
+ | 'order.created'
221
+ | 'order.completed'
222
+ | 'order.cancelled'
223
+ | 'payment.succeeded'
224
+ | 'payment.failed'
225
+ | 'subscription.created'
226
+ | 'subscription.cancelled';
227
+ ```
228
+
229
+ ## Database Schema
230
+
231
+ ```sql
232
+ -- Endpoints
233
+ CREATE TABLE webhook_endpoints (
234
+ id UUID PRIMARY KEY,
235
+ url TEXT NOT NULL,
236
+ secret TEXT NOT NULL,
237
+ events TEXT[] NOT NULL,
238
+ headers JSONB,
239
+ enabled BOOLEAN DEFAULT true,
240
+ description TEXT,
241
+ created_at TIMESTAMP,
242
+ updated_at TIMESTAMP
243
+ );
244
+
245
+ -- Deliveries
246
+ CREATE TABLE webhook_deliveries (
247
+ id UUID PRIMARY KEY,
248
+ endpoint_id UUID REFERENCES webhook_endpoints,
249
+ event_type TEXT NOT NULL,
250
+ payload JSONB NOT NULL,
251
+ status TEXT NOT NULL,
252
+ attempts INTEGER DEFAULT 0,
253
+ max_attempts INTEGER DEFAULT 5,
254
+ delivered_at TIMESTAMP,
255
+ next_retry_at TIMESTAMP,
256
+ response_status INTEGER,
257
+ response_body TEXT,
258
+ error TEXT,
259
+ created_at TIMESTAMP
260
+ );
261
+ ```
262
+
263
+ ## Best Practices
264
+
265
+ 1. **Idempotency** - Include delivery ID for idempotent processing
266
+ 2. **Timeout Handling** - Set reasonable timeouts (10s recommended)
267
+ 3. **Signature Verification** - Always verify signatures on receipt
268
+ 4. **Event Filtering** - Subscribe only to needed events
269
+ 5. **Retry Handling** - Implement idempotent handlers for retries
270
+ 6. **Secret Rotation** - Rotate secrets periodically