@nestarc/webhook 0.5.0 → 0.7.0
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/README.md +21 -0
- package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -1
- package/dist/adapters/prisma-delivery.repository.js +1 -0
- package/dist/adapters/prisma-delivery.repository.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/webhook-options.interface.d.ts +23 -0
- package/dist/interfaces/webhook-options.interface.d.ts.map +1 -1
- package/dist/ports/webhook-delivery.repository.d.ts +1 -0
- package/dist/ports/webhook-delivery.repository.d.ts.map +1 -1
- package/dist/webhook.circuit-breaker.d.ts +5 -1
- package/dist/webhook.circuit-breaker.d.ts.map +1 -1
- package/dist/webhook.circuit-breaker.js +16 -1
- package/dist/webhook.circuit-breaker.js.map +1 -1
- package/dist/webhook.delivery-worker.d.ts +2 -0
- package/dist/webhook.delivery-worker.d.ts.map +1 -1
- package/dist/webhook.delivery-worker.js +21 -1
- package/dist/webhook.delivery-worker.js.map +1 -1
- package/dist/webhook.url-validator.d.ts +9 -1
- package/dist/webhook.url-validator.d.ts.map +1 -1
- package/dist/webhook.url-validator.js +36 -21
- package/dist/webhook.url-validator.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circui
|
|
|
19
19
|
- **SSRF defense** — DNS resolution validation at registration and dispatch time
|
|
20
20
|
- **Ports/adapters architecture** — swap Prisma or fetch with custom implementations
|
|
21
21
|
- **Stale delivery recovery** — lease-based reaper recovers crashed worker deliveries
|
|
22
|
+
- **Notification hooks** — `onDeliveryFailed` and `onEndpointDisabled` callbacks for custom alerting
|
|
22
23
|
|
|
23
24
|
## Installation
|
|
24
25
|
|
|
@@ -185,6 +186,8 @@ export class WebhookController {
|
|
|
185
186
|
| `polling.staleSendingMinutes` | `5` | Minutes before a stuck SENDING delivery is recovered |
|
|
186
187
|
| `allowPrivateUrls` | `false` | Allow private/internal URLs (dev/test only) |
|
|
187
188
|
| `secretVault` | `PlaintextSecretVault` | Custom vault for encrypting/decrypting endpoint secrets at rest |
|
|
189
|
+
| `onDeliveryFailed` | — | Fire-and-forget callback when a delivery exhausts all retries. Receives `DeliveryFailedContext` (`tenantId` is `null` for global endpoints). |
|
|
190
|
+
| `onEndpointDisabled` | — | Fire-and-forget callback when the circuit breaker disables an endpoint. Fires once at exact threshold crossing. |
|
|
188
191
|
|
|
189
192
|
### Custom adapters
|
|
190
193
|
|
|
@@ -238,6 +241,24 @@ webhook-signature: v1,<base64-hmac-sha256>
|
|
|
238
241
|
- HTTP redirects are disabled (`redirect: 'manual'`)
|
|
239
242
|
- Use `allowPrivateUrls: true` for local development only
|
|
240
243
|
|
|
244
|
+
**Structured validation errors** — validation failures throw `WebhookUrlValidationError` (subclass of `Error`) with a machine-readable `reason`:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { WebhookUrlValidationError } from '@nestarc/webhook';
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
await endpointAdmin.createEndpoint({ url, events: ['*'] });
|
|
251
|
+
} catch (err) {
|
|
252
|
+
if (err instanceof WebhookUrlValidationError) {
|
|
253
|
+
// err.reason: 'parse' | 'scheme' | 'blocked_hostname'
|
|
254
|
+
// | 'loopback' | 'private' | 'link_local' | 'invalid_target'
|
|
255
|
+
// err.url, err.resolvedIp also available
|
|
256
|
+
throw new BadRequestException({ message: err.message, reason: err.reason });
|
|
257
|
+
}
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
241
262
|
### Secret handling
|
|
242
263
|
|
|
243
264
|
- Signing secrets are excluded from read queries (`listEndpoints`, `getEndpoint`)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,yBAAyB,EAC1B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,EACf,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,qBACa,wBAAyB,YAAW,yBAAyB;IAEtE,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBADN,MAAM,EAAE,GAAG,EACX,KAAK,CAAC,EAAE,kBAAkB,YAAA;IAGvC,6BAA6B,CACjC,EAAE,EAAE,GAAG,EACP,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAUV,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBrE,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"prisma-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,yBAAyB,EAC1B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,EACf,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,qBACa,wBAAyB,YAAW,yBAAyB;IAEtE,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBADN,MAAM,EAAE,GAAG,EACX,KAAK,CAAC,EAAE,kBAAkB,YAAA;IAGvC,6BAA6B,CACjC,EAAE,EAAE,GAAG,EACP,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAUV,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBrE,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAoBnE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvF,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,IAAI,CAAC;IAYV,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAY9D,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC;IAgDtB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQnD,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK7E"}
|
|
@@ -49,6 +49,7 @@ let PrismaDeliveryRepository = class PrismaDeliveryRepository {
|
|
|
49
49
|
const rows = await this.prisma.$queryRaw `
|
|
50
50
|
SELECT
|
|
51
51
|
d.id, d.event_id, d.endpoint_id, d.attempts, d.max_attempts,
|
|
52
|
+
e.tenant_id::text AS tenant_id,
|
|
52
53
|
e.url, e.secret,
|
|
53
54
|
ev.event_type, ev.payload
|
|
54
55
|
FROM webhook_deliveries d
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma-delivery.repository.js","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAarC,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IAEhB;IACA;IAFnB,YACmB,MAAW,EACX,KAA0B;QAD1B,WAAM,GAAN,MAAM,CAAK;QACX,UAAK,GAAL,KAAK,CAAqB;IAC1C,CAAC;IAEJ,KAAK,CAAC,6BAA6B,CACjC,EAAO,EACP,OAAe,EACf,WAAqB,EACrB,WAAmB;QAEnB,MAAM,EAAE,CAAC,iBAAiB,CACxB;oEAC8D,EAC9D,OAAO,EACP,WAAW,EACX,WAAW,CACZ,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAI,EAA+B;QACvD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,sBAAsB,CAAC,SAAiB;QAC5C,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAmB;;;;;;;;;gBASnC,SAAS;;;;;;;;wCAQe,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,WAAqB;QAC1C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAmB
|
|
1
|
+
{"version":3,"file":"prisma-delivery.repository.js","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,9 +20,9 @@ export { PrismaEndpointRepository } from './adapters/prisma-endpoint.repository'
|
|
|
20
20
|
export { PrismaDeliveryRepository } from './adapters/prisma-delivery.repository';
|
|
21
21
|
export { FetchHttpClient } from './adapters/fetch-http-client';
|
|
22
22
|
export { PlaintextSecretVault } from './adapters/plaintext-secret-vault';
|
|
23
|
-
export type { WebhookModuleOptions, WebhookModuleAsyncOptions, WebhookOptionsFactory, DeliveryOptions, CircuitBreakerOptions, PollingOptions, } from './interfaces/webhook-options.interface';
|
|
23
|
+
export type { WebhookModuleOptions, WebhookModuleAsyncOptions, WebhookOptionsFactory, DeliveryOptions, CircuitBreakerOptions, PollingOptions, DeliveryFailedContext, EndpointDisabledContext, } from './interfaces/webhook-options.interface';
|
|
24
24
|
export type { EndpointRecord, EndpointRecordWithSecret, CreateEndpointDto, UpdateEndpointDto, } from './interfaces/webhook-endpoint.interface';
|
|
25
25
|
export type { DeliveryStatus, DeliveryRecord, EventRecord, DeliveryResult, DeliveryLogFilters, } from './interfaces/webhook-delivery.interface';
|
|
26
26
|
export { WEBHOOK_MODULE_OPTIONS, WEBHOOK_EVENT_REPOSITORY, WEBHOOK_ENDPOINT_REPOSITORY, WEBHOOK_DELIVERY_REPOSITORY, WEBHOOK_HTTP_CLIENT, DEFAULT_BACKOFF_SCHEDULE, DEFAULT_DELIVERY_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_POLLING_INTERVAL, DEFAULT_POLLING_BATCH_SIZE, DEFAULT_CIRCUIT_BREAKER_THRESHOLD, DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES, DEFAULT_STALE_SENDING_MINUTES, } from './webhook.constants';
|
|
27
|
-
export { validateWebhookUrl, resolveAndValidateHost } from './webhook.url-validator';
|
|
27
|
+
export { validateWebhookUrl, resolveAndValidateHost, WebhookUrlValidationError, type WebhookUrlValidationReason, } from './webhook.url-validator';
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,kFAAkF;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,YAAY,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,YAAY,EACV,yBAAyB,EACzB,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,YAAY,EACV,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,kFAAkF;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,YAAY,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,YAAY,EACV,yBAAyB,EACzB,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,YAAY,EACV,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAGhD,YAAY,EACV,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AAEjD,YAAY,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,GACnB,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,iCAAiC,EACjC,wCAAwC,EACxC,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,0BAA0B,GAChC,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveAndValidateHost = exports.validateWebhookUrl = exports.DEFAULT_STALE_SENDING_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_THRESHOLD = exports.DEFAULT_POLLING_BATCH_SIZE = exports.DEFAULT_POLLING_INTERVAL = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_DELIVERY_TIMEOUT = exports.DEFAULT_BACKOFF_SCHEDULE = exports.WEBHOOK_HTTP_CLIENT = exports.WEBHOOK_DELIVERY_REPOSITORY = exports.WEBHOOK_ENDPOINT_REPOSITORY = exports.WEBHOOK_EVENT_REPOSITORY = exports.WEBHOOK_MODULE_OPTIONS = exports.PlaintextSecretVault = exports.FetchHttpClient = exports.PrismaDeliveryRepository = exports.PrismaEndpointRepository = exports.PrismaEventRepository = exports.WebhookAdminService = exports.WebhookDeliveryAdminService = exports.WebhookEndpointAdminService = exports.WebhookEvent = exports.WebhookCircuitBreaker = exports.WebhookSigner = exports.WebhookRetryPolicy = exports.WebhookDispatcher = exports.WebhookDeliveryWorker = exports.WebhookService = exports.WebhookModule = void 0;
|
|
3
|
+
exports.WebhookUrlValidationError = exports.resolveAndValidateHost = exports.validateWebhookUrl = exports.DEFAULT_STALE_SENDING_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_THRESHOLD = exports.DEFAULT_POLLING_BATCH_SIZE = exports.DEFAULT_POLLING_INTERVAL = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_DELIVERY_TIMEOUT = exports.DEFAULT_BACKOFF_SCHEDULE = exports.WEBHOOK_HTTP_CLIENT = exports.WEBHOOK_DELIVERY_REPOSITORY = exports.WEBHOOK_ENDPOINT_REPOSITORY = exports.WEBHOOK_EVENT_REPOSITORY = exports.WEBHOOK_MODULE_OPTIONS = exports.PlaintextSecretVault = exports.FetchHttpClient = exports.PrismaDeliveryRepository = exports.PrismaEndpointRepository = exports.PrismaEventRepository = exports.WebhookAdminService = exports.WebhookDeliveryAdminService = exports.WebhookEndpointAdminService = exports.WebhookEvent = exports.WebhookCircuitBreaker = exports.WebhookSigner = exports.WebhookRetryPolicy = exports.WebhookDispatcher = exports.WebhookDeliveryWorker = exports.WebhookService = exports.WebhookModule = void 0;
|
|
4
4
|
// Module
|
|
5
5
|
var webhook_module_1 = require("./webhook.module");
|
|
6
6
|
Object.defineProperty(exports, "WebhookModule", { enumerable: true, get: function () { return webhook_module_1.WebhookModule; } });
|
|
@@ -56,4 +56,5 @@ Object.defineProperty(exports, "DEFAULT_STALE_SENDING_MINUTES", { enumerable: tr
|
|
|
56
56
|
var webhook_url_validator_1 = require("./webhook.url-validator");
|
|
57
57
|
Object.defineProperty(exports, "validateWebhookUrl", { enumerable: true, get: function () { return webhook_url_validator_1.validateWebhookUrl; } });
|
|
58
58
|
Object.defineProperty(exports, "resolveAndValidateHost", { enumerable: true, get: function () { return webhook_url_validator_1.resolveAndValidateHost; } });
|
|
59
|
+
Object.defineProperty(exports, "WebhookUrlValidationError", { enumerable: true, get: function () { return webhook_url_validator_1.WebhookUrlValidationError; } });
|
|
59
60
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AAEtB,gBAAgB;AAChB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,2DAAyD;AAAhD,uHAAA,iBAAiB,OAAA;AAC1B,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAC3B,mDAAwE;AAA/D,+GAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,iBAAiB;AACjB,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,kFAAkF;AAClF,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAY5B,mBAAmB;AACnB,8EAA2E;AAAlE,gIAAA,qBAAqB,OAAA;AAC9B,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,kEAA+D;AAAtD,oHAAA,eAAe,OAAA;AACxB,4EAAyE;AAAhE,8HAAA,oBAAoB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AAEtB,gBAAgB;AAChB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,2DAAyD;AAAhD,uHAAA,iBAAiB,OAAA;AAC1B,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAC3B,mDAAwE;AAA/D,+GAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,iBAAiB;AACjB,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,kFAAkF;AAClF,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAY5B,mBAAmB;AACnB,8EAA2E;AAAlE,gIAAA,qBAAqB,OAAA;AAC9B,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,kEAA+D;AAAtD,oHAAA,eAAe,OAAA;AACxB,4EAAyE;AAAhE,8HAAA,oBAAoB,OAAA;AA8B7B,qBAAqB;AACrB,yDAc6B;AAb3B,2HAAA,sBAAsB,OAAA;AACtB,6HAAA,wBAAwB,OAAA;AACxB,gIAAA,2BAA2B,OAAA;AAC3B,gIAAA,2BAA2B,OAAA;AAC3B,wHAAA,mBAAmB,OAAA;AACnB,6HAAA,wBAAwB,OAAA;AACxB,6HAAA,wBAAwB,OAAA;AACxB,wHAAA,mBAAmB,OAAA;AACnB,6HAAA,wBAAwB,OAAA;AACxB,+HAAA,0BAA0B,OAAA;AAC1B,sIAAA,iCAAiC,OAAA;AACjC,6IAAA,wCAAwC,OAAA;AACxC,kIAAA,6BAA6B,OAAA;AAG/B,iEAKiC;AAJ/B,2HAAA,kBAAkB,OAAA;AAClB,+HAAA,sBAAsB,OAAA;AACtB,kIAAA,yBAAyB,OAAA"}
|
|
@@ -22,6 +22,25 @@ export interface PollingOptions {
|
|
|
22
22
|
/** Minutes before a SENDING delivery is considered stale and reset to PENDING. Default: 5 */
|
|
23
23
|
staleSendingMinutes?: number;
|
|
24
24
|
}
|
|
25
|
+
export interface DeliveryFailedContext {
|
|
26
|
+
deliveryId: string;
|
|
27
|
+
endpointId: string;
|
|
28
|
+
eventId: string;
|
|
29
|
+
/** Null when the endpoint is not scoped to a tenant. */
|
|
30
|
+
tenantId: string | null;
|
|
31
|
+
attempts: number;
|
|
32
|
+
maxAttempts: number;
|
|
33
|
+
lastError: string | null;
|
|
34
|
+
responseStatus: number | null;
|
|
35
|
+
}
|
|
36
|
+
export interface EndpointDisabledContext {
|
|
37
|
+
endpointId: string;
|
|
38
|
+
/** Null when the endpoint is not scoped to a tenant. */
|
|
39
|
+
tenantId: string | null;
|
|
40
|
+
url: string;
|
|
41
|
+
reason: string;
|
|
42
|
+
consecutiveFailures: number;
|
|
43
|
+
}
|
|
25
44
|
export interface WebhookModuleOptions {
|
|
26
45
|
/** PrismaClient instance — used by default Prisma adapters. Not needed if all custom repositories are provided. */
|
|
27
46
|
prisma?: any;
|
|
@@ -37,6 +56,10 @@ export interface WebhookModuleOptions {
|
|
|
37
56
|
httpClient?: WebhookHttpClient;
|
|
38
57
|
/** Custom secret vault for encrypting/decrypting endpoint signing secrets at rest. Default: PlaintextSecretVault (no-op). */
|
|
39
58
|
secretVault?: WebhookSecretVault;
|
|
59
|
+
/** Called when a delivery exhausts all retry attempts. Fire-and-forget — errors are logged, not propagated. */
|
|
60
|
+
onDeliveryFailed?: (context: DeliveryFailedContext) => void | Promise<void>;
|
|
61
|
+
/** Called when the circuit breaker disables an endpoint. Fire-and-forget — errors are logged, not propagated. */
|
|
62
|
+
onEndpointDisabled?: (context: EndpointDisabledContext) => void | Promise<void>;
|
|
40
63
|
}
|
|
41
64
|
export interface WebhookOptionsFactory {
|
|
42
65
|
createWebhookOptions(): Promise<WebhookModuleOptions> | WebhookModuleOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-options.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/webhook-options.interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,sIAAsI;IACtI,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,mHAAmH;IACnH,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,cAAc,CAAC,EAAE,qBAAqB,CAAC;IACvC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sFAAsF;IACtF,eAAe,CAAC,EAAE,sBAAsB,CAAC;IACzC,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;IAC/C,kBAAkB,CAAC,EAAE,yBAAyB,CAAC;IAC/C,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,6HAA6H;IAC7H,WAAW,CAAC,EAAE,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook-options.interface.d.ts","sourceRoot":"","sources":["../../src/interfaces/webhook-options.interface.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/ports/webhook-delivery.repository.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,EACf,MAAM,0CAA0C,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,6BAA6B,CAC3B,EAAE,EAAE,OAAO,EACX,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACtE,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAEpE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErG,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC7F,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE"}
|
|
1
|
+
{"version":3,"file":"webhook-delivery.repository.d.ts","sourceRoot":"","sources":["../../src/ports/webhook-delivery.repository.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,EACf,MAAM,0CAA0C,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,6BAA6B,CAC3B,EAAE,EAAE,OAAO,EACX,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEjE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACtE,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAEpE,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErG,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAC7F,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE"}
|
|
@@ -2,11 +2,15 @@ import { WebhookModuleOptions } from './interfaces/webhook-options.interface';
|
|
|
2
2
|
import { WebhookEndpointRepository } from './ports/webhook-endpoint.repository';
|
|
3
3
|
export declare class WebhookCircuitBreaker {
|
|
4
4
|
private readonly endpointRepo;
|
|
5
|
+
private readonly options;
|
|
5
6
|
private readonly logger;
|
|
6
7
|
private readonly failureThreshold;
|
|
7
8
|
private readonly cooldownMinutes;
|
|
8
9
|
constructor(endpointRepo: WebhookEndpointRepository, options: WebhookModuleOptions);
|
|
9
|
-
afterDelivery(endpointId: string, success: boolean
|
|
10
|
+
afterDelivery(endpointId: string, success: boolean, meta?: {
|
|
11
|
+
tenantId: string | null;
|
|
12
|
+
url: string;
|
|
13
|
+
}): Promise<void>;
|
|
10
14
|
recoverEligibleEndpoints(): Promise<number>;
|
|
11
15
|
}
|
|
12
16
|
//# sourceMappingURL=webhook.circuit-breaker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.circuit-breaker.d.ts","sourceRoot":"","sources":["../src/webhook.circuit-breaker.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEhF,qBACa,qBAAqB;IAO9B,OAAO,CAAC,QAAQ,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"webhook.circuit-breaker.d.ts","sourceRoot":"","sources":["../src/webhook.circuit-breaker.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEhF,qBACa,qBAAqB;IAO9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAItB,YAAY,EAAE,yBAAyB,EAEvC,OAAO,EAAE,oBAAoB;IAQ1C,aAAa,CACjB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAC9C,OAAO,CAAC,IAAI,CAAC;IAgCV,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC;CASlD"}
|
|
@@ -18,17 +18,19 @@ const common_1 = require("@nestjs/common");
|
|
|
18
18
|
const webhook_constants_1 = require("./webhook.constants");
|
|
19
19
|
let WebhookCircuitBreaker = WebhookCircuitBreaker_1 = class WebhookCircuitBreaker {
|
|
20
20
|
endpointRepo;
|
|
21
|
+
options;
|
|
21
22
|
logger = new common_1.Logger(WebhookCircuitBreaker_1.name);
|
|
22
23
|
failureThreshold;
|
|
23
24
|
cooldownMinutes;
|
|
24
25
|
constructor(endpointRepo, options) {
|
|
25
26
|
this.endpointRepo = endpointRepo;
|
|
27
|
+
this.options = options;
|
|
26
28
|
this.failureThreshold =
|
|
27
29
|
options.circuitBreaker?.failureThreshold ?? webhook_constants_1.DEFAULT_CIRCUIT_BREAKER_THRESHOLD;
|
|
28
30
|
this.cooldownMinutes =
|
|
29
31
|
options.circuitBreaker?.cooldownMinutes ?? webhook_constants_1.DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES;
|
|
30
32
|
}
|
|
31
|
-
async afterDelivery(endpointId, success) {
|
|
33
|
+
async afterDelivery(endpointId, success, meta) {
|
|
32
34
|
if (success) {
|
|
33
35
|
await this.endpointRepo.resetFailures(endpointId);
|
|
34
36
|
}
|
|
@@ -37,6 +39,19 @@ let WebhookCircuitBreaker = WebhookCircuitBreaker_1 = class WebhookCircuitBreake
|
|
|
37
39
|
if (failures >= this.failureThreshold) {
|
|
38
40
|
await this.endpointRepo.disableEndpoint(endpointId, 'consecutive_failures_exceeded');
|
|
39
41
|
this.logger.warn(`Endpoint ${endpointId} disabled: consecutive_failures_exceeded (threshold=${this.failureThreshold})`);
|
|
42
|
+
// Fire hook only at exact threshold crossing to prevent duplicate notifications
|
|
43
|
+
// in multi-instance environments where concurrent failures exceed the threshold.
|
|
44
|
+
if (failures === this.failureThreshold && this.options.onEndpointDisabled) {
|
|
45
|
+
void Promise.resolve(this.options.onEndpointDisabled({
|
|
46
|
+
endpointId,
|
|
47
|
+
tenantId: meta?.tenantId ?? null,
|
|
48
|
+
url: meta?.url ?? '',
|
|
49
|
+
reason: 'consecutive_failures_exceeded',
|
|
50
|
+
consecutiveFailures: failures,
|
|
51
|
+
})).catch((hookError) => {
|
|
52
|
+
this.logger.error(`onEndpointDisabled callback error: ${hookError}`);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
40
55
|
}
|
|
41
56
|
}
|
|
42
57
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.circuit-breaker.js","sourceRoot":"","sources":["../src/webhook.circuit-breaker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,2DAK6B;AAKtB,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAOb;
|
|
1
|
+
{"version":3,"file":"webhook.circuit-breaker.js","sourceRoot":"","sources":["../src/webhook.circuit-breaker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4D;AAC5D,2DAK6B;AAKtB,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAOb;IAEA;IARF,MAAM,GAAG,IAAI,eAAM,CAAC,uBAAqB,CAAC,IAAI,CAAC,CAAC;IAChD,gBAAgB,CAAS;IACzB,eAAe,CAAS;IAEzC,YAEmB,YAAuC,EAEvC,OAA6B;QAF7B,iBAAY,GAAZ,YAAY,CAA2B;QAEvC,YAAO,GAAP,OAAO,CAAsB;QAE9C,IAAI,CAAC,gBAAgB;YACnB,OAAO,CAAC,cAAc,EAAE,gBAAgB,IAAI,qDAAiC,CAAC;QAChF,IAAI,CAAC,eAAe;YAClB,OAAO,CAAC,cAAc,EAAE,eAAe,IAAI,4DAAwC,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,UAAkB,EAClB,OAAgB,EAChB,IAA+C;QAE/C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YACvE,IAAI,QAAQ,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACtC,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CACrC,UAAU,EACV,+BAA+B,CAChC,CAAC;gBACF,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,YAAY,UAAU,uDAAuD,IAAI,CAAC,gBAAgB,GAAG,CACtG,CAAC;gBACF,gFAAgF;gBAChF,iFAAiF;gBACjF,IAAI,QAAQ,KAAK,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;oBAC1E,KAAK,OAAO,CAAC,OAAO,CAClB,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC;wBAC9B,UAAU;wBACV,QAAQ,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI;wBAChC,GAAG,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE;wBACpB,MAAM,EAAE,+BAA+B;wBACvC,mBAAmB,EAAE,QAAQ;qBAC9B,CAAC,CACH,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE;wBACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,SAAS,EAAE,CAAC,CAAC;oBACvE,CAAC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,wBAAwB;QAC5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,wBAAwB,CAC5D,IAAI,CAAC,eAAe,CACrB,CAAC;QACF,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,KAAK,6BAA6B,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF,CAAA;AA9DY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,mBAAU,GAAE;IAOR,WAAA,IAAA,eAAM,EAAC,+CAA2B,CAAC,CAAA;IAEnC,WAAA,IAAA,eAAM,EAAC,0CAAsB,CAAC,CAAA;;GARtB,qBAAqB,CA8DjC"}
|
|
@@ -9,6 +9,7 @@ export declare class WebhookDeliveryWorker implements OnModuleDestroy {
|
|
|
9
9
|
private readonly dispatcher;
|
|
10
10
|
private readonly retryPolicy;
|
|
11
11
|
private readonly circuitBreaker;
|
|
12
|
+
private readonly options;
|
|
12
13
|
private readonly logger;
|
|
13
14
|
private readonly batchSize;
|
|
14
15
|
private readonly staleSendingMinutes;
|
|
@@ -18,6 +19,7 @@ export declare class WebhookDeliveryWorker implements OnModuleDestroy {
|
|
|
18
19
|
constructor(deliveryRepo: WebhookDeliveryRepository, dispatcher: WebhookDispatcher, retryPolicy: WebhookRetryPolicy, circuitBreaker: WebhookCircuitBreaker, options: WebhookModuleOptions);
|
|
19
20
|
poll(): Promise<void>;
|
|
20
21
|
private processDelivery;
|
|
22
|
+
private fireDeliveryFailedHook;
|
|
21
23
|
onModuleDestroy(): Promise<void>;
|
|
22
24
|
}
|
|
23
25
|
//# sourceMappingURL=webhook.delivery-worker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.delivery-worker.d.ts","sourceRoot":"","sources":["../src/webhook.delivery-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAO5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAEL,yBAAyB,EAC1B,MAAM,qCAAqC,CAAC;AAE7C,qBACa,qBAAsB,YAAW,eAAe;IAUzD,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"webhook.delivery-worker.d.ts","sourceRoot":"","sources":["../src/webhook.delivery-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAO5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAEL,yBAAyB,EAC1B,MAAM,qCAAqC,CAAC;AAE7C,qBACa,qBAAsB,YAAW,eAAe;IAUzD,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAd1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAK;gBAIV,YAAY,EAAE,yBAAyB,EACvC,UAAU,EAAE,iBAAiB,EAC7B,WAAW,EAAE,kBAAkB,EAC/B,cAAc,EAAE,qBAAqB,EAErC,OAAO,EAAE,oBAAoB;IAO1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAmCb,eAAe;IAoE7B,OAAO,CAAC,sBAAsB;IAuBxB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CAevC"}
|
|
@@ -24,6 +24,7 @@ let WebhookDeliveryWorker = WebhookDeliveryWorker_1 = class WebhookDeliveryWorke
|
|
|
24
24
|
dispatcher;
|
|
25
25
|
retryPolicy;
|
|
26
26
|
circuitBreaker;
|
|
27
|
+
options;
|
|
27
28
|
logger = new common_1.Logger(WebhookDeliveryWorker_1.name);
|
|
28
29
|
batchSize;
|
|
29
30
|
staleSendingMinutes;
|
|
@@ -35,6 +36,7 @@ let WebhookDeliveryWorker = WebhookDeliveryWorker_1 = class WebhookDeliveryWorke
|
|
|
35
36
|
this.dispatcher = dispatcher;
|
|
36
37
|
this.retryPolicy = retryPolicy;
|
|
37
38
|
this.circuitBreaker = circuitBreaker;
|
|
39
|
+
this.options = options;
|
|
38
40
|
this.batchSize = options.polling?.batchSize ?? webhook_constants_1.DEFAULT_POLLING_BATCH_SIZE;
|
|
39
41
|
this.staleSendingMinutes =
|
|
40
42
|
options.polling?.staleSendingMinutes ?? webhook_constants_1.DEFAULT_STALE_SENDING_MINUTES;
|
|
@@ -77,6 +79,7 @@ let WebhookDeliveryWorker = WebhookDeliveryWorker_1 = class WebhookDeliveryWorke
|
|
|
77
79
|
else if (newAttempts >= delivery.max_attempts) {
|
|
78
80
|
await this.deliveryRepo.markFailed(delivery.id, newAttempts, result);
|
|
79
81
|
this.logger.warn(`Delivery ${delivery.id} exhausted retries (${newAttempts}/${delivery.max_attempts})`);
|
|
82
|
+
this.fireDeliveryFailedHook(delivery, newAttempts, result.error ?? null, result.statusCode ?? null);
|
|
80
83
|
}
|
|
81
84
|
else {
|
|
82
85
|
const nextAt = this.retryPolicy.nextAttemptAt(newAttempts);
|
|
@@ -84,7 +87,7 @@ let WebhookDeliveryWorker = WebhookDeliveryWorker_1 = class WebhookDeliveryWorke
|
|
|
84
87
|
}
|
|
85
88
|
// Circuit breaker — failure here must NOT revert delivery state
|
|
86
89
|
try {
|
|
87
|
-
await this.circuitBreaker.afterDelivery(delivery.endpoint_id, result.success);
|
|
90
|
+
await this.circuitBreaker.afterDelivery(delivery.endpoint_id, result.success, { tenantId: delivery.tenant_id, url: delivery.url });
|
|
88
91
|
}
|
|
89
92
|
catch (cbError) {
|
|
90
93
|
this.logger.error(`Circuit breaker update failed for ${delivery.endpoint_id}: ${cbError}`);
|
|
@@ -103,6 +106,7 @@ let WebhookDeliveryWorker = WebhookDeliveryWorker_1 = class WebhookDeliveryWorke
|
|
|
103
106
|
if (newAttempts >= delivery.max_attempts) {
|
|
104
107
|
await this.deliveryRepo.markFailed(delivery.id, newAttempts, errorResult);
|
|
105
108
|
this.logger.warn(`Delivery ${delivery.id} exhausted retries on exception (${newAttempts}/${delivery.max_attempts})`);
|
|
109
|
+
this.fireDeliveryFailedHook(delivery, newAttempts, errorResult.error ?? null, null);
|
|
106
110
|
}
|
|
107
111
|
else {
|
|
108
112
|
const nextAt = this.retryPolicy.nextAttemptAt(newAttempts);
|
|
@@ -117,6 +121,22 @@ let WebhookDeliveryWorker = WebhookDeliveryWorker_1 = class WebhookDeliveryWorke
|
|
|
117
121
|
this.activeDeliveries--;
|
|
118
122
|
}
|
|
119
123
|
}
|
|
124
|
+
fireDeliveryFailedHook(delivery, attempts, lastError, responseStatus) {
|
|
125
|
+
if (!this.options.onDeliveryFailed)
|
|
126
|
+
return;
|
|
127
|
+
void Promise.resolve(this.options.onDeliveryFailed({
|
|
128
|
+
deliveryId: delivery.id,
|
|
129
|
+
endpointId: delivery.endpoint_id,
|
|
130
|
+
eventId: delivery.event_id,
|
|
131
|
+
tenantId: delivery.tenant_id,
|
|
132
|
+
attempts,
|
|
133
|
+
maxAttempts: delivery.max_attempts,
|
|
134
|
+
lastError,
|
|
135
|
+
responseStatus,
|
|
136
|
+
})).catch((hookError) => {
|
|
137
|
+
this.logger.error(`onDeliveryFailed callback error: ${hookError}`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
120
140
|
async onModuleDestroy() {
|
|
121
141
|
this.isShuttingDown = true;
|
|
122
142
|
this.logger.log('Shutting down delivery worker...');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.delivery-worker.js","sourceRoot":"","sources":["../src/webhook.delivery-worker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA6E;AAC7E,uEAAkE;AAClE,6DAAyD;AACzD,iEAA4D;AAC5D,2DAK6B;AAQtB,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAUb;IACA;IACA;IACA;
|
|
1
|
+
{"version":3,"file":"webhook.delivery-worker.js","sourceRoot":"","sources":["../src/webhook.delivery-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export type WebhookUrlValidationReason = 'parse' | 'scheme' | 'blocked_hostname' | 'loopback' | 'private' | 'link_local' | 'invalid_target';
|
|
2
|
+
export declare class WebhookUrlValidationError extends Error {
|
|
3
|
+
readonly name = "WebhookUrlValidationError";
|
|
4
|
+
readonly reason: WebhookUrlValidationReason;
|
|
5
|
+
readonly url?: string;
|
|
6
|
+
readonly resolvedIp?: string;
|
|
7
|
+
constructor(message: string, reason: WebhookUrlValidationReason, url?: string, resolvedIp?: string);
|
|
8
|
+
}
|
|
1
9
|
export declare function validateWebhookUrl(url: string): Promise<void>;
|
|
2
|
-
export declare function resolveAndValidateHost(hostname: string): Promise<void>;
|
|
10
|
+
export declare function resolveAndValidateHost(hostname: string, url?: string): Promise<void>;
|
|
3
11
|
//# sourceMappingURL=webhook.url-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.url-validator.d.ts","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"AAUA,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"webhook.url-validator.d.ts","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,0BAA0B,GAClC,OAAO,GACP,QAAQ,GACR,kBAAkB,GAClB,UAAU,GACV,SAAS,GACT,YAAY,GACZ,gBAAgB,CAAC;AAErB,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,0BAA0B,EAClC,GAAG,CAAC,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM;CAQtB;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCnE;AA+GD,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WebhookUrlValidationError = void 0;
|
|
36
37
|
exports.validateWebhookUrl = validateWebhookUrl;
|
|
37
38
|
exports.resolveAndValidateHost = resolveAndValidateHost;
|
|
38
39
|
const net = __importStar(require("net"));
|
|
@@ -43,57 +44,71 @@ const BLOCKED_HOSTNAMES = new Set([
|
|
|
43
44
|
'ip6-localhost',
|
|
44
45
|
'ip6-loopback',
|
|
45
46
|
]);
|
|
47
|
+
class WebhookUrlValidationError extends Error {
|
|
48
|
+
name = 'WebhookUrlValidationError';
|
|
49
|
+
reason;
|
|
50
|
+
url;
|
|
51
|
+
resolvedIp;
|
|
52
|
+
constructor(message, reason, url, resolvedIp) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.reason = reason;
|
|
55
|
+
this.url = url;
|
|
56
|
+
this.resolvedIp = resolvedIp;
|
|
57
|
+
Object.setPrototypeOf(this, WebhookUrlValidationError.prototype);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.WebhookUrlValidationError = WebhookUrlValidationError;
|
|
46
61
|
async function validateWebhookUrl(url) {
|
|
47
62
|
let parsed;
|
|
48
63
|
try {
|
|
49
64
|
parsed = new URL(url);
|
|
50
65
|
}
|
|
51
66
|
catch {
|
|
52
|
-
throw new
|
|
67
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: unable to parse "${url}"`, 'parse', url);
|
|
53
68
|
}
|
|
54
69
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
55
|
-
throw new
|
|
70
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: scheme must be http or https, got "${parsed.protocol}"`, 'scheme', url);
|
|
56
71
|
}
|
|
57
72
|
const hostname = parsed.hostname.toLowerCase();
|
|
58
73
|
if (BLOCKED_HOSTNAMES.has(hostname)) {
|
|
59
|
-
throw new
|
|
74
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${hostname}" is not allowed (loopback address)`, 'blocked_hostname', url);
|
|
60
75
|
}
|
|
61
76
|
if (net.isIPv4(hostname)) {
|
|
62
|
-
validateIPv4(hostname);
|
|
77
|
+
validateIPv4(hostname, url);
|
|
63
78
|
}
|
|
64
79
|
else if (net.isIPv6(hostname) || hostname.startsWith('[')) {
|
|
65
80
|
const cleanIp = hostname.replace(/^\[|\]$/g, '');
|
|
66
|
-
validateIPv6(cleanIp);
|
|
81
|
+
validateIPv6(cleanIp, url);
|
|
67
82
|
}
|
|
68
83
|
else {
|
|
69
84
|
// Hostname — resolve DNS and validate all resolved IPs
|
|
70
|
-
await resolveAndValidateHost(hostname);
|
|
85
|
+
await resolveAndValidateHost(hostname, url);
|
|
71
86
|
}
|
|
72
87
|
}
|
|
73
|
-
function validateIPv4(ip) {
|
|
88
|
+
function validateIPv4(ip, url) {
|
|
74
89
|
const parts = ip.split('.').map(Number);
|
|
75
90
|
if (parts[0] === 127) {
|
|
76
|
-
throw new
|
|
91
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a loopback address`, 'loopback', url, ip);
|
|
77
92
|
}
|
|
78
93
|
if (parts[0] === 10) {
|
|
79
|
-
throw new
|
|
94
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
80
95
|
}
|
|
81
96
|
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) {
|
|
82
|
-
throw new
|
|
97
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
83
98
|
}
|
|
84
99
|
if (parts[0] === 192 && parts[1] === 168) {
|
|
85
|
-
throw new
|
|
100
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
86
101
|
}
|
|
87
102
|
if (parts[0] === 169 && parts[1] === 254) {
|
|
88
|
-
throw new
|
|
103
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a link-local/metadata address`, 'link_local', url, ip);
|
|
89
104
|
}
|
|
90
105
|
if (parts[0] === 0) {
|
|
91
|
-
throw new
|
|
106
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is not a valid target`, 'invalid_target', url, ip);
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
|
-
function validateIPv6(ip) {
|
|
109
|
+
function validateIPv6(ip, url) {
|
|
95
110
|
if (ip === '::1') {
|
|
96
|
-
throw new
|
|
111
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a loopback address`, 'loopback', url, ip);
|
|
97
112
|
}
|
|
98
113
|
const lowerIp = ip.toLowerCase();
|
|
99
114
|
// IPv4-mapped IPv6 — ::ffff:x.x.x.x or ::ffff:HHHH:HHHH (hex form)
|
|
@@ -116,19 +131,19 @@ function validateIPv6(ip) {
|
|
|
116
131
|
return; // Unknown format, let it through (conservative)
|
|
117
132
|
}
|
|
118
133
|
}
|
|
119
|
-
validateIPv4(ipv4);
|
|
134
|
+
validateIPv4(ipv4, url);
|
|
120
135
|
return;
|
|
121
136
|
}
|
|
122
137
|
const firstSegment = ip.split(':')[0].toLowerCase();
|
|
123
138
|
if (firstSegment.startsWith('fc') || firstSegment.startsWith('fd')) {
|
|
124
|
-
throw new
|
|
139
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
125
140
|
}
|
|
126
141
|
if (firstSegment.startsWith('fe8') || firstSegment.startsWith('fe9') ||
|
|
127
142
|
firstSegment.startsWith('fea') || firstSegment.startsWith('feb')) {
|
|
128
|
-
throw new
|
|
143
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a link-local address`, 'link_local', url, ip);
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
|
-
async function resolveAndValidateHost(hostname) {
|
|
146
|
+
async function resolveAndValidateHost(hostname, url) {
|
|
132
147
|
let addresses = [];
|
|
133
148
|
try {
|
|
134
149
|
const ipv4 = await dns.promises.resolve4(hostname);
|
|
@@ -146,10 +161,10 @@ async function resolveAndValidateHost(hostname) {
|
|
|
146
161
|
}
|
|
147
162
|
for (const ip of addresses) {
|
|
148
163
|
if (net.isIPv4(ip)) {
|
|
149
|
-
validateIPv4(ip); // throws if private
|
|
164
|
+
validateIPv4(ip, url); // throws if private
|
|
150
165
|
}
|
|
151
166
|
else if (net.isIPv6(ip)) {
|
|
152
|
-
validateIPv6(ip); // throws if private
|
|
167
|
+
validateIPv6(ip, url); // throws if private
|
|
153
168
|
}
|
|
154
169
|
}
|
|
155
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestarc/webhook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circuit breaker, delivery logs, fan-out, Standard Webhooks compatible",
|
|
5
5
|
"author": "nestarc",
|
|
6
6
|
"license": "MIT",
|