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.
- package/.claude/settings.local.json +30 -0
- package/.github/CODEOWNERS +18 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
- package/.github/dependabot.yml +59 -0
- package/.github/workflows/ci.yml +188 -0
- package/.github/workflows/release.yml +195 -0
- package/AUDIT.md +602 -0
- package/LICENSE +21 -0
- package/README.md +1102 -1
- package/dist/cli/index.cjs +2026 -2168
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2026 -2168
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +595 -616
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -52
- package/dist/index.d.ts +114 -52
- package/dist/index.js +595 -616
- package/dist/index.js.map +1 -1
- package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
- package/docs/DATABASE_MULTI_ORM.md +399 -0
- package/docs/PHASE1_BREAKDOWN.md +346 -0
- package/docs/PROGRESS.md +550 -0
- package/docs/modules/ANALYTICS.md +226 -0
- package/docs/modules/API-VERSIONING.md +252 -0
- package/docs/modules/AUDIT.md +192 -0
- package/docs/modules/AUTH.md +431 -0
- package/docs/modules/CACHE.md +346 -0
- package/docs/modules/EMAIL.md +254 -0
- package/docs/modules/FEATURE-FLAG.md +291 -0
- package/docs/modules/I18N.md +294 -0
- package/docs/modules/MEDIA-PROCESSING.md +281 -0
- package/docs/modules/MFA.md +266 -0
- package/docs/modules/NOTIFICATION.md +311 -0
- package/docs/modules/OAUTH.md +237 -0
- package/docs/modules/PAYMENT.md +804 -0
- package/docs/modules/QUEUE.md +540 -0
- package/docs/modules/RATE-LIMIT.md +339 -0
- package/docs/modules/SEARCH.md +288 -0
- package/docs/modules/SECURITY.md +327 -0
- package/docs/modules/SESSION.md +382 -0
- package/docs/modules/SWAGGER.md +305 -0
- package/docs/modules/UPLOAD.md +296 -0
- package/docs/modules/USER.md +505 -0
- package/docs/modules/VALIDATION.md +294 -0
- package/docs/modules/WEBHOOK.md +270 -0
- package/docs/modules/WEBSOCKET.md +691 -0
- package/package.json +53 -38
- package/prisma/schema.prisma +395 -1
- package/src/cli/commands/add-module.ts +520 -87
- package/src/cli/commands/db.ts +3 -4
- package/src/cli/commands/docs.ts +256 -6
- package/src/cli/commands/generate.ts +12 -19
- package/src/cli/commands/init.ts +384 -214
- package/src/cli/index.ts +0 -4
- package/src/cli/templates/repository.ts +6 -1
- package/src/cli/templates/routes.ts +6 -21
- package/src/cli/utils/docs-generator.ts +6 -7
- package/src/cli/utils/env-manager.ts +717 -0
- package/src/cli/utils/field-parser.ts +16 -7
- package/src/cli/utils/interactive-prompt.ts +223 -0
- package/src/cli/utils/template-manager.ts +346 -0
- package/src/config/database.config.ts +183 -0
- package/src/config/env.ts +0 -10
- package/src/config/index.ts +0 -14
- package/src/core/server.ts +1 -1
- package/src/database/adapters/mongoose.adapter.ts +132 -0
- package/src/database/adapters/prisma.adapter.ts +118 -0
- package/src/database/connection.ts +190 -0
- package/src/database/interfaces/database.interface.ts +85 -0
- package/src/database/interfaces/index.ts +7 -0
- package/src/database/interfaces/repository.interface.ts +129 -0
- package/src/database/models/mongoose/index.ts +7 -0
- package/src/database/models/mongoose/payment.schema.ts +347 -0
- package/src/database/models/mongoose/user.schema.ts +154 -0
- package/src/database/prisma.ts +1 -4
- package/src/database/redis.ts +101 -0
- package/src/database/repositories/mongoose/index.ts +7 -0
- package/src/database/repositories/mongoose/payment.repository.ts +380 -0
- package/src/database/repositories/mongoose/user.repository.ts +255 -0
- package/src/database/seed.ts +6 -1
- package/src/index.ts +9 -20
- package/src/middleware/security.ts +2 -6
- package/src/modules/analytics/analytics.routes.ts +80 -0
- package/src/modules/analytics/analytics.service.ts +364 -0
- package/src/modules/analytics/index.ts +18 -0
- package/src/modules/analytics/types.ts +180 -0
- package/src/modules/api-versioning/index.ts +15 -0
- package/src/modules/api-versioning/types.ts +86 -0
- package/src/modules/api-versioning/versioning.middleware.ts +120 -0
- package/src/modules/api-versioning/versioning.routes.ts +54 -0
- package/src/modules/api-versioning/versioning.service.ts +189 -0
- package/src/modules/audit/audit.repository.ts +206 -0
- package/src/modules/audit/audit.service.ts +27 -59
- package/src/modules/auth/auth.controller.ts +2 -2
- package/src/modules/auth/auth.middleware.ts +3 -9
- package/src/modules/auth/auth.routes.ts +10 -107
- package/src/modules/auth/auth.service.ts +126 -23
- package/src/modules/auth/index.ts +3 -4
- package/src/modules/cache/cache.service.ts +367 -0
- package/src/modules/cache/index.ts +10 -0
- package/src/modules/cache/types.ts +44 -0
- package/src/modules/email/email.service.ts +3 -10
- package/src/modules/email/templates.ts +2 -8
- package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
- package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
- package/src/modules/feature-flag/feature-flag.service.ts +566 -0
- package/src/modules/feature-flag/index.ts +20 -0
- package/src/modules/feature-flag/types.ts +192 -0
- package/src/modules/i18n/i18n.middleware.ts +186 -0
- package/src/modules/i18n/i18n.routes.ts +191 -0
- package/src/modules/i18n/i18n.service.ts +456 -0
- package/src/modules/i18n/index.ts +18 -0
- package/src/modules/i18n/types.ts +118 -0
- package/src/modules/media-processing/index.ts +17 -0
- package/src/modules/media-processing/media-processing.routes.ts +111 -0
- package/src/modules/media-processing/media-processing.service.ts +245 -0
- package/src/modules/media-processing/types.ts +156 -0
- package/src/modules/mfa/index.ts +20 -0
- package/src/modules/mfa/mfa.repository.ts +206 -0
- package/src/modules/mfa/mfa.routes.ts +595 -0
- package/src/modules/mfa/mfa.service.ts +572 -0
- package/src/modules/mfa/totp.ts +150 -0
- package/src/modules/mfa/types.ts +57 -0
- package/src/modules/notification/index.ts +20 -0
- package/src/modules/notification/notification.repository.ts +356 -0
- package/src/modules/notification/notification.service.ts +483 -0
- package/src/modules/notification/types.ts +119 -0
- package/src/modules/oauth/index.ts +20 -0
- package/src/modules/oauth/oauth.repository.ts +219 -0
- package/src/modules/oauth/oauth.routes.ts +446 -0
- package/src/modules/oauth/oauth.service.ts +293 -0
- package/src/modules/oauth/providers/apple.provider.ts +250 -0
- package/src/modules/oauth/providers/facebook.provider.ts +181 -0
- package/src/modules/oauth/providers/github.provider.ts +248 -0
- package/src/modules/oauth/providers/google.provider.ts +189 -0
- package/src/modules/oauth/providers/twitter.provider.ts +214 -0
- package/src/modules/oauth/types.ts +94 -0
- package/src/modules/payment/index.ts +19 -0
- package/src/modules/payment/payment.repository.ts +733 -0
- package/src/modules/payment/payment.routes.ts +390 -0
- package/src/modules/payment/payment.service.ts +354 -0
- package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
- package/src/modules/payment/providers/paypal.provider.ts +190 -0
- package/src/modules/payment/providers/stripe.provider.ts +215 -0
- package/src/modules/payment/types.ts +140 -0
- package/src/modules/queue/cron.ts +438 -0
- package/src/modules/queue/index.ts +87 -0
- package/src/modules/queue/queue.routes.ts +600 -0
- package/src/modules/queue/queue.service.ts +842 -0
- package/src/modules/queue/types.ts +222 -0
- package/src/modules/queue/workers.ts +366 -0
- package/src/modules/rate-limit/index.ts +59 -0
- package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
- package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
- package/src/modules/rate-limit/rate-limit.service.ts +348 -0
- package/src/modules/rate-limit/stores/memory.store.ts +165 -0
- package/src/modules/rate-limit/stores/redis.store.ts +322 -0
- package/src/modules/rate-limit/types.ts +153 -0
- package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
- package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
- package/src/modules/search/adapters/memory.adapter.ts +278 -0
- package/src/modules/search/index.ts +21 -0
- package/src/modules/search/search.service.ts +234 -0
- package/src/modules/search/types.ts +214 -0
- package/src/modules/security/index.ts +40 -0
- package/src/modules/security/sanitize.ts +223 -0
- package/src/modules/security/security-audit.service.ts +388 -0
- package/src/modules/security/security.middleware.ts +398 -0
- package/src/modules/session/index.ts +3 -0
- package/src/modules/session/session.repository.ts +159 -0
- package/src/modules/session/session.service.ts +340 -0
- package/src/modules/session/types.ts +38 -0
- package/src/modules/swagger/index.ts +7 -1
- package/src/modules/swagger/schema-builder.ts +16 -4
- package/src/modules/swagger/swagger.service.ts +9 -10
- package/src/modules/swagger/types.ts +0 -2
- package/src/modules/upload/index.ts +14 -0
- package/src/modules/upload/types.ts +83 -0
- package/src/modules/upload/upload.repository.ts +199 -0
- package/src/modules/upload/upload.routes.ts +311 -0
- package/src/modules/upload/upload.service.ts +448 -0
- package/src/modules/user/index.ts +3 -3
- package/src/modules/user/user.controller.ts +15 -9
- package/src/modules/user/user.repository.ts +237 -113
- package/src/modules/user/user.routes.ts +39 -164
- package/src/modules/user/user.service.ts +4 -3
- package/src/modules/validation/validator.ts +12 -17
- package/src/modules/webhook/index.ts +91 -0
- package/src/modules/webhook/retry.ts +196 -0
- package/src/modules/webhook/signature.ts +135 -0
- package/src/modules/webhook/types.ts +181 -0
- package/src/modules/webhook/webhook.repository.ts +358 -0
- package/src/modules/webhook/webhook.routes.ts +442 -0
- package/src/modules/webhook/webhook.service.ts +457 -0
- package/src/modules/websocket/features.ts +504 -0
- package/src/modules/websocket/index.ts +106 -0
- package/src/modules/websocket/middlewares.ts +298 -0
- package/src/modules/websocket/types.ts +181 -0
- package/src/modules/websocket/websocket.service.ts +692 -0
- package/src/utils/errors.ts +7 -0
- package/src/utils/pagination.ts +4 -1
- package/tests/helpers/db-check.ts +79 -0
- package/tests/integration/auth-redis.test.ts +94 -0
- package/tests/integration/cache-redis.test.ts +387 -0
- package/tests/integration/mongoose-repositories.test.ts +410 -0
- package/tests/integration/payment-prisma.test.ts +637 -0
- package/tests/integration/queue-bullmq.test.ts +417 -0
- package/tests/integration/user-prisma.test.ts +441 -0
- package/tests/integration/websocket-socketio.test.ts +552 -0
- package/tests/setup.ts +11 -9
- package/vitest.config.ts +3 -8
- package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
- package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
- package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
- package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
- package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
import { Router, type Request } from 'express';
|
|
3
|
+
import type { WebhookService } from './webhook.service.js';
|
|
4
|
+
import type { WebhookEventType } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create webhook management routes
|
|
8
|
+
* These routes should be protected with authentication/authorization
|
|
9
|
+
*/
|
|
10
|
+
export function createWebhookRoutes(service: WebhookService): Router {
|
|
11
|
+
const router = Router();
|
|
12
|
+
|
|
13
|
+
// Endpoint Management
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* POST /webhooks/endpoints
|
|
17
|
+
* Create a new webhook endpoint
|
|
18
|
+
*/
|
|
19
|
+
router.post('/endpoints', async (req: Request, res: Response): Promise<void> => {
|
|
20
|
+
try {
|
|
21
|
+
const { url, events, description, headers, metadata } = req.body;
|
|
22
|
+
|
|
23
|
+
if (!url || !events || !Array.isArray(events)) {
|
|
24
|
+
res.status(400).json({
|
|
25
|
+
error: 'Bad Request',
|
|
26
|
+
message: 'url and events array are required',
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const endpoint = await service.createEndpoint({
|
|
32
|
+
url,
|
|
33
|
+
events,
|
|
34
|
+
description,
|
|
35
|
+
headers,
|
|
36
|
+
metadata,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
res.status(201).json({
|
|
40
|
+
success: true,
|
|
41
|
+
data: endpoint,
|
|
42
|
+
});
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('[WebhookRoutes] Error creating endpoint:', error);
|
|
45
|
+
res.status(500).json({
|
|
46
|
+
error: 'Internal Server Error',
|
|
47
|
+
message: error instanceof Error ? error.message : 'Failed to create webhook endpoint',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* GET /webhooks/endpoints
|
|
54
|
+
* List all webhook endpoints
|
|
55
|
+
*/
|
|
56
|
+
router.get('/endpoints', async (_req: Request, res: Response) => {
|
|
57
|
+
try {
|
|
58
|
+
const endpoints = await service.listEndpoints();
|
|
59
|
+
|
|
60
|
+
res.json({
|
|
61
|
+
success: true,
|
|
62
|
+
data: endpoints,
|
|
63
|
+
count: endpoints.length,
|
|
64
|
+
});
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('[WebhookRoutes] Error listing endpoints:', error);
|
|
67
|
+
res.status(500).json({
|
|
68
|
+
error: 'Internal Server Error',
|
|
69
|
+
message: 'Failed to list webhook endpoints',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* GET /webhooks/endpoints/:id
|
|
76
|
+
* Get a specific webhook endpoint
|
|
77
|
+
*/
|
|
78
|
+
router.get('/endpoints/:id', async (req: Request, res: Response): Promise<void> => {
|
|
79
|
+
try {
|
|
80
|
+
const { id } = req.params;
|
|
81
|
+
if (!id) {
|
|
82
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const endpoint = await service.getEndpoint(id);
|
|
86
|
+
|
|
87
|
+
res.json({
|
|
88
|
+
success: true,
|
|
89
|
+
data: endpoint,
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
93
|
+
res.status(404).json({
|
|
94
|
+
error: 'Not Found',
|
|
95
|
+
message: 'Webhook endpoint not found',
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.error('[WebhookRoutes] Error getting endpoint:', error);
|
|
101
|
+
res.status(500).json({
|
|
102
|
+
error: 'Internal Server Error',
|
|
103
|
+
message: 'Failed to get webhook endpoint',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* PATCH /webhooks/endpoints/:id
|
|
110
|
+
* Update a webhook endpoint
|
|
111
|
+
*/
|
|
112
|
+
router.patch('/endpoints/:id', async (req: Request, res: Response): Promise<void> => {
|
|
113
|
+
try {
|
|
114
|
+
const { id } = req.params;
|
|
115
|
+
if (!id) {
|
|
116
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const { url, events, enabled, description, headers, metadata } = req.body;
|
|
120
|
+
|
|
121
|
+
const endpoint = await service.updateEndpoint(id, {
|
|
122
|
+
url,
|
|
123
|
+
events,
|
|
124
|
+
enabled,
|
|
125
|
+
description,
|
|
126
|
+
headers,
|
|
127
|
+
metadata,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
res.json({
|
|
131
|
+
success: true,
|
|
132
|
+
data: endpoint,
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
136
|
+
res.status(404).json({
|
|
137
|
+
error: 'Not Found',
|
|
138
|
+
message: 'Webhook endpoint not found',
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.error('[WebhookRoutes] Error updating endpoint:', error);
|
|
144
|
+
res.status(500).json({
|
|
145
|
+
error: 'Internal Server Error',
|
|
146
|
+
message: error instanceof Error ? error.message : 'Failed to update webhook endpoint',
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* DELETE /webhooks/endpoints/:id
|
|
153
|
+
* Delete a webhook endpoint
|
|
154
|
+
*/
|
|
155
|
+
router.delete('/endpoints/:id', async (req: Request, res: Response): Promise<void> => {
|
|
156
|
+
try {
|
|
157
|
+
const { id } = req.params;
|
|
158
|
+
if (!id) {
|
|
159
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
await service.deleteEndpoint(id);
|
|
163
|
+
|
|
164
|
+
res.json({
|
|
165
|
+
success: true,
|
|
166
|
+
message: 'Webhook endpoint deleted',
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
170
|
+
res.status(404).json({
|
|
171
|
+
error: 'Not Found',
|
|
172
|
+
message: 'Webhook endpoint not found',
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.error('[WebhookRoutes] Error deleting endpoint:', error);
|
|
178
|
+
res.status(500).json({
|
|
179
|
+
error: 'Internal Server Error',
|
|
180
|
+
message: 'Failed to delete webhook endpoint',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* POST /webhooks/endpoints/:id/rotate-secret
|
|
187
|
+
* Rotate webhook endpoint secret
|
|
188
|
+
*/
|
|
189
|
+
router.post(
|
|
190
|
+
'/endpoints/:id/rotate-secret',
|
|
191
|
+
async (req: Request, res: Response): Promise<void> => {
|
|
192
|
+
try {
|
|
193
|
+
const { id } = req.params;
|
|
194
|
+
if (!id) {
|
|
195
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const endpoint = await service.rotateSecret(id);
|
|
199
|
+
|
|
200
|
+
res.json({
|
|
201
|
+
success: true,
|
|
202
|
+
data: endpoint,
|
|
203
|
+
message: 'Secret rotated successfully',
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
207
|
+
res.status(404).json({
|
|
208
|
+
error: 'Not Found',
|
|
209
|
+
message: 'Webhook endpoint not found',
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.error('[WebhookRoutes] Error rotating secret:', error);
|
|
215
|
+
res.status(500).json({
|
|
216
|
+
error: 'Internal Server Error',
|
|
217
|
+
message: 'Failed to rotate webhook secret',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// Event Publishing
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* POST /webhooks/events
|
|
227
|
+
* Publish a webhook event
|
|
228
|
+
*/
|
|
229
|
+
router.post('/events', async (req: Request, res: Response): Promise<void> => {
|
|
230
|
+
try {
|
|
231
|
+
const { type, payload, endpoints } = req.body;
|
|
232
|
+
|
|
233
|
+
if (!type || !payload) {
|
|
234
|
+
res.status(400).json({
|
|
235
|
+
error: 'Bad Request',
|
|
236
|
+
message: 'type and payload are required',
|
|
237
|
+
});
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const event = await service.publishEvent(type as WebhookEventType, payload, endpoints);
|
|
242
|
+
|
|
243
|
+
res.status(201).json({
|
|
244
|
+
success: true,
|
|
245
|
+
data: event,
|
|
246
|
+
message: 'Event published successfully',
|
|
247
|
+
});
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.error('[WebhookRoutes] Error publishing event:', error);
|
|
250
|
+
res.status(500).json({
|
|
251
|
+
error: 'Internal Server Error',
|
|
252
|
+
message: 'Failed to publish webhook event',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Delivery Management
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* GET /webhooks/deliveries
|
|
261
|
+
* List webhook deliveries with optional filters
|
|
262
|
+
*/
|
|
263
|
+
router.get('/deliveries', async (req: Request, res: Response) => {
|
|
264
|
+
try {
|
|
265
|
+
const { endpointId, eventType, status, startDate, endDate, limit, offset } = req.query;
|
|
266
|
+
|
|
267
|
+
const deliveries = await service.listDeliveries({
|
|
268
|
+
endpointId: endpointId as string,
|
|
269
|
+
eventType: eventType as WebhookEventType,
|
|
270
|
+
status: status as 'pending' | 'success' | 'failed' | 'retrying',
|
|
271
|
+
startDate: startDate ? new Date(startDate as string) : undefined,
|
|
272
|
+
endDate: endDate ? new Date(endDate as string) : undefined,
|
|
273
|
+
limit: limit ? parseInt(limit as string, 10) : undefined,
|
|
274
|
+
offset: offset ? parseInt(offset as string, 10) : undefined,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
res.json({
|
|
278
|
+
success: true,
|
|
279
|
+
data: deliveries,
|
|
280
|
+
count: deliveries.length,
|
|
281
|
+
});
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error('[WebhookRoutes] Error listing deliveries:', error);
|
|
284
|
+
res.status(500).json({
|
|
285
|
+
error: 'Internal Server Error',
|
|
286
|
+
message: 'Failed to list webhook deliveries',
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* GET /webhooks/deliveries/:id
|
|
293
|
+
* Get a specific webhook delivery
|
|
294
|
+
*/
|
|
295
|
+
router.get('/deliveries/:id', async (req: Request, res: Response): Promise<void> => {
|
|
296
|
+
try {
|
|
297
|
+
const { id } = req.params;
|
|
298
|
+
if (!id) {
|
|
299
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const delivery = await service.getDelivery(id);
|
|
303
|
+
|
|
304
|
+
res.json({
|
|
305
|
+
success: true,
|
|
306
|
+
data: delivery,
|
|
307
|
+
});
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
310
|
+
res.status(404).json({
|
|
311
|
+
error: 'Not Found',
|
|
312
|
+
message: 'Webhook delivery not found',
|
|
313
|
+
});
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
console.error('[WebhookRoutes] Error getting delivery:', error);
|
|
318
|
+
res.status(500).json({
|
|
319
|
+
error: 'Internal Server Error',
|
|
320
|
+
message: 'Failed to get webhook delivery',
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* GET /webhooks/deliveries/:id/attempts
|
|
327
|
+
* Get delivery attempts for a specific delivery
|
|
328
|
+
*/
|
|
329
|
+
router.get('/deliveries/:id/attempts', async (req: Request, res: Response): Promise<void> => {
|
|
330
|
+
try {
|
|
331
|
+
const { id } = req.params;
|
|
332
|
+
if (!id) {
|
|
333
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const attempts = await service.getDeliveryAttempts(id);
|
|
337
|
+
|
|
338
|
+
res.json({
|
|
339
|
+
success: true,
|
|
340
|
+
data: attempts,
|
|
341
|
+
count: attempts.length,
|
|
342
|
+
});
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error('[WebhookRoutes] Error getting delivery attempts:', error);
|
|
345
|
+
res.status(500).json({
|
|
346
|
+
error: 'Internal Server Error',
|
|
347
|
+
message: 'Failed to get delivery attempts',
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* POST /webhooks/deliveries/:id/retry
|
|
354
|
+
* Manually retry a failed delivery
|
|
355
|
+
*/
|
|
356
|
+
router.post('/deliveries/:id/retry', async (req: Request, res: Response): Promise<void> => {
|
|
357
|
+
try {
|
|
358
|
+
const { id } = req.params;
|
|
359
|
+
if (!id) {
|
|
360
|
+
res.status(400).json({ error: 'Bad Request', message: 'id is required' });
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
await service.retryDelivery(id);
|
|
364
|
+
|
|
365
|
+
res.json({
|
|
366
|
+
success: true,
|
|
367
|
+
message: 'Delivery retry initiated',
|
|
368
|
+
});
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
371
|
+
res.status(404).json({
|
|
372
|
+
error: 'Not Found',
|
|
373
|
+
message: 'Webhook delivery not found',
|
|
374
|
+
});
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (error instanceof Error && error.message.includes('Cannot retry')) {
|
|
379
|
+
res.status(400).json({
|
|
380
|
+
error: 'Bad Request',
|
|
381
|
+
message: error.message,
|
|
382
|
+
});
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
console.error('[WebhookRoutes] Error retrying delivery:', error);
|
|
387
|
+
res.status(500).json({
|
|
388
|
+
error: 'Internal Server Error',
|
|
389
|
+
message: 'Failed to retry webhook delivery',
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// Statistics
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* GET /webhooks/stats
|
|
398
|
+
* Get webhook statistics
|
|
399
|
+
*/
|
|
400
|
+
router.get('/stats', async (req: Request, res: Response) => {
|
|
401
|
+
try {
|
|
402
|
+
const { endpointId } = req.query;
|
|
403
|
+
const stats = await service.getStats(endpointId as string);
|
|
404
|
+
|
|
405
|
+
res.json({
|
|
406
|
+
success: true,
|
|
407
|
+
data: stats,
|
|
408
|
+
});
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error('[WebhookRoutes] Error getting stats:', error);
|
|
411
|
+
res.status(500).json({
|
|
412
|
+
error: 'Internal Server Error',
|
|
413
|
+
message: 'Failed to get webhook statistics',
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* POST /webhooks/cleanup
|
|
420
|
+
* Cleanup old webhook data
|
|
421
|
+
*/
|
|
422
|
+
router.post('/cleanup', async (req: Request, res: Response) => {
|
|
423
|
+
try {
|
|
424
|
+
const { olderThanDays = 30 } = req.body;
|
|
425
|
+
const cleaned = await service.cleanup(olderThanDays);
|
|
426
|
+
|
|
427
|
+
res.json({
|
|
428
|
+
success: true,
|
|
429
|
+
message: `Cleaned up ${cleaned} old records`,
|
|
430
|
+
cleaned,
|
|
431
|
+
});
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error('[WebhookRoutes] Error cleaning up:', error);
|
|
434
|
+
res.status(500).json({
|
|
435
|
+
error: 'Internal Server Error',
|
|
436
|
+
message: 'Failed to cleanup webhook data',
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
return router;
|
|
442
|
+
}
|