@nestarc/webhook 0.2.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/LICENSE +21 -0
- package/README.md +281 -0
- package/dist/adapters/fetch-http-client.d.ts +6 -0
- package/dist/adapters/fetch-http-client.d.ts.map +1 -0
- package/dist/adapters/fetch-http-client.js +56 -0
- package/dist/adapters/fetch-http-client.js.map +1 -0
- package/dist/adapters/prisma-delivery.repository.d.ts +18 -0
- package/dist/adapters/prisma-delivery.repository.d.ts.map +1 -0
- package/dist/adapters/prisma-delivery.repository.js +162 -0
- package/dist/adapters/prisma-delivery.repository.js.map +1 -0
- package/dist/adapters/prisma-endpoint.repository.d.ts +18 -0
- package/dist/adapters/prisma-endpoint.repository.d.ts.map +1 -0
- package/dist/adapters/prisma-endpoint.repository.js +157 -0
- package/dist/adapters/prisma-endpoint.repository.js.map +1 -0
- package/dist/adapters/prisma-event.repository.d.ts +8 -0
- package/dist/adapters/prisma-event.repository.d.ts.map +1 -0
- package/dist/adapters/prisma-event.repository.js +39 -0
- package/dist/adapters/prisma-event.repository.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/webhook-delivery.interface.d.ts +39 -0
- package/dist/interfaces/webhook-delivery.interface.d.ts.map +1 -0
- package/dist/interfaces/webhook-delivery.interface.js +3 -0
- package/dist/interfaces/webhook-delivery.interface.js.map +1 -0
- package/dist/interfaces/webhook-endpoint.interface.d.ts +34 -0
- package/dist/interfaces/webhook-endpoint.interface.d.ts.map +1 -0
- package/dist/interfaces/webhook-endpoint.interface.js +3 -0
- package/dist/interfaces/webhook-endpoint.interface.js.map +1 -0
- package/dist/interfaces/webhook-options.interface.d.ts +45 -0
- package/dist/interfaces/webhook-options.interface.d.ts.map +1 -0
- package/dist/interfaces/webhook-options.interface.js +3 -0
- package/dist/interfaces/webhook-options.interface.js.map +1 -0
- package/dist/ports/webhook-delivery.repository.d.ts +26 -0
- package/dist/ports/webhook-delivery.repository.d.ts.map +1 -0
- package/dist/ports/webhook-delivery.repository.js +3 -0
- package/dist/ports/webhook-delivery.repository.js.map +1 -0
- package/dist/ports/webhook-endpoint.repository.d.ts +15 -0
- package/dist/ports/webhook-endpoint.repository.d.ts.map +1 -0
- package/dist/ports/webhook-endpoint.repository.js +3 -0
- package/dist/ports/webhook-endpoint.repository.js.map +1 -0
- package/dist/ports/webhook-event.repository.d.ts +5 -0
- package/dist/ports/webhook-event.repository.d.ts.map +1 -0
- package/dist/ports/webhook-event.repository.js +3 -0
- package/dist/ports/webhook-event.repository.js.map +1 -0
- package/dist/ports/webhook-http-client.d.ts +5 -0
- package/dist/ports/webhook-http-client.d.ts.map +1 -0
- package/dist/ports/webhook-http-client.js +3 -0
- package/dist/ports/webhook-http-client.js.map +1 -0
- package/dist/webhook.admin.service.d.ts +22 -0
- package/dist/webhook.admin.service.d.ts.map +1 -0
- package/dist/webhook.admin.service.js +58 -0
- package/dist/webhook.admin.service.js.map +1 -0
- package/dist/webhook.circuit-breaker.d.ts +12 -0
- package/dist/webhook.circuit-breaker.d.ts.map +1 -0
- package/dist/webhook.circuit-breaker.js +58 -0
- package/dist/webhook.circuit-breaker.js.map +1 -0
- package/dist/webhook.constants.d.ts +17 -0
- package/dist/webhook.constants.d.ts.map +1 -0
- package/dist/webhook.constants.js +26 -0
- package/dist/webhook.constants.js.map +1 -0
- package/dist/webhook.delivery-admin.service.d.ts +9 -0
- package/dist/webhook.delivery-admin.service.d.ts.map +1 -0
- package/dist/webhook.delivery-admin.service.js +36 -0
- package/dist/webhook.delivery-admin.service.js.map +1 -0
- package/dist/webhook.delivery-worker.d.ts +23 -0
- package/dist/webhook.delivery-worker.d.ts.map +1 -0
- package/dist/webhook.delivery-worker.js +141 -0
- package/dist/webhook.delivery-worker.js.map +1 -0
- package/dist/webhook.dispatcher.d.ts +14 -0
- package/dist/webhook.dispatcher.d.ts.map +1 -0
- package/dist/webhook.dispatcher.js +54 -0
- package/dist/webhook.dispatcher.js.map +1 -0
- package/dist/webhook.endpoint-admin.service.d.ts +23 -0
- package/dist/webhook.endpoint-admin.service.d.ts.map +1 -0
- package/dist/webhook.endpoint-admin.service.js +97 -0
- package/dist/webhook.endpoint-admin.service.js.map +1 -0
- package/dist/webhook.event.d.ts +6 -0
- package/dist/webhook.event.d.ts.map +1 -0
- package/dist/webhook.event.js +22 -0
- package/dist/webhook.event.js.map +1 -0
- package/dist/webhook.module.d.ts +15 -0
- package/dist/webhook.module.d.ts.map +1 -0
- package/dist/webhook.module.js +176 -0
- package/dist/webhook.module.js.map +1 -0
- package/dist/webhook.retry-policy.d.ts +7 -0
- package/dist/webhook.retry-policy.d.ts.map +1 -0
- package/dist/webhook.retry-policy.js +38 -0
- package/dist/webhook.retry-policy.js.map +1 -0
- package/dist/webhook.service.d.ts +17 -0
- package/dist/webhook.service.d.ts.map +1 -0
- package/dist/webhook.service.js +63 -0
- package/dist/webhook.service.js.map +1 -0
- package/dist/webhook.signer.d.ts +12 -0
- package/dist/webhook.signer.d.ts.map +1 -0
- package/dist/webhook.signer.js +75 -0
- package/dist/webhook.signer.js.map +1 -0
- package/dist/webhook.url-validator.d.ts +3 -0
- package/dist/webhook.url-validator.d.ts.map +1 -0
- package/dist/webhook.url-validator.js +156 -0
- package/dist/webhook.url-validator.js.map +1 -0
- package/package.json +61 -0
- package/src/sql/create-webhook-tables.sql +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nestarc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# @nestarc/webhook
|
|
2
|
+
|
|
3
|
+
Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circuit breaker, delivery logs, fan-out, [Standard Webhooks](https://www.standardwebhooks.com/) compatible.
|
|
4
|
+
|
|
5
|
+
**No separate infrastructure required.** Uses your existing PostgreSQL database.
|
|
6
|
+
|
|
7
|
+
[](https://github.com/nestarc/webhook/actions/workflows/ci.yml)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Fan-out delivery** — one event to many endpoints
|
|
12
|
+
- **HMAC-SHA256 signing** — Standard Webhooks compatible headers
|
|
13
|
+
- **Exponential backoff** — 30s, 5m, 30m, 2h, 24h (with jitter)
|
|
14
|
+
- **Circuit breaker** — auto-disable failing endpoints, auto-recover after cooldown
|
|
15
|
+
- **Dead letter queue** — failed deliveries tracked for manual retry
|
|
16
|
+
- **Delivery logs** — full audit trail (status code, latency, response body)
|
|
17
|
+
- **Multi-instance safe** — `FOR UPDATE SKIP LOCKED` prevents duplicate delivery
|
|
18
|
+
- **Graceful shutdown** — waits for in-flight deliveries on process exit
|
|
19
|
+
- **SSRF defense** — DNS resolution validation at registration and dispatch time
|
|
20
|
+
- **Ports/adapters architecture** — swap Prisma or fetch with custom implementations
|
|
21
|
+
- **Stale delivery recovery** — lease-based reaper recovers crashed worker deliveries
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @nestarc/webhook
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Peer dependencies:**
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @nestjs/common @nestjs/core @nestjs/schedule @prisma/client
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Database Setup
|
|
36
|
+
|
|
37
|
+
Run the migration SQL against your PostgreSQL database:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
psql -d your_database -f node_modules/@nestarc/webhook/src/sql/create-webhook-tables.sql
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This creates three tables: `webhook_endpoints`, `webhook_events`, `webhook_deliveries`.
|
|
44
|
+
|
|
45
|
+
The migration includes `CREATE EXTENSION IF NOT EXISTS pgcrypto` for PostgreSQL < 13 compatibility.
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
### 1. Register the module
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { WebhookModule } from '@nestarc/webhook';
|
|
53
|
+
|
|
54
|
+
@Module({
|
|
55
|
+
imports: [
|
|
56
|
+
WebhookModule.forRoot({
|
|
57
|
+
prisma: prismaService,
|
|
58
|
+
delivery: {
|
|
59
|
+
timeout: 10_000,
|
|
60
|
+
maxRetries: 5,
|
|
61
|
+
backoff: 'exponential',
|
|
62
|
+
jitter: true,
|
|
63
|
+
},
|
|
64
|
+
circuitBreaker: {
|
|
65
|
+
failureThreshold: 5,
|
|
66
|
+
cooldownMinutes: 60,
|
|
67
|
+
},
|
|
68
|
+
polling: {
|
|
69
|
+
interval: 5000,
|
|
70
|
+
batchSize: 50,
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
],
|
|
74
|
+
})
|
|
75
|
+
export class AppModule {}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. Define events
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { WebhookEvent } from '@nestarc/webhook';
|
|
82
|
+
|
|
83
|
+
export class OrderCreatedEvent extends WebhookEvent {
|
|
84
|
+
static readonly eventType = 'order.created';
|
|
85
|
+
|
|
86
|
+
constructor(
|
|
87
|
+
public readonly orderId: string,
|
|
88
|
+
public readonly total: number,
|
|
89
|
+
) {
|
|
90
|
+
super();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> **Note:** Subclasses **must** define `static readonly eventType`. The module throws at runtime if this is missing.
|
|
96
|
+
|
|
97
|
+
### 3. Send events
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { WebhookService } from '@nestarc/webhook';
|
|
101
|
+
|
|
102
|
+
@Injectable()
|
|
103
|
+
export class OrderService {
|
|
104
|
+
constructor(private readonly webhooks: WebhookService) {}
|
|
105
|
+
|
|
106
|
+
async createOrder(dto: CreateOrderDto) {
|
|
107
|
+
const order = await this.saveOrder(dto);
|
|
108
|
+
await this.webhooks.send(new OrderCreatedEvent(order.id, order.total));
|
|
109
|
+
return order;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 4. Manage endpoints
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { WebhookEndpointAdminService } from '@nestarc/webhook';
|
|
118
|
+
|
|
119
|
+
@Injectable()
|
|
120
|
+
export class WebhookController {
|
|
121
|
+
constructor(private readonly endpointAdmin: WebhookEndpointAdminService) {}
|
|
122
|
+
|
|
123
|
+
async register() {
|
|
124
|
+
// Secret is returned only on creation
|
|
125
|
+
return this.endpointAdmin.createEndpoint({
|
|
126
|
+
url: 'https://customer.com/webhooks',
|
|
127
|
+
events: ['order.created', 'order.paid'],
|
|
128
|
+
secret: 'auto',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## API Reference
|
|
135
|
+
|
|
136
|
+
### WebhookService
|
|
137
|
+
|
|
138
|
+
| Method | Description |
|
|
139
|
+
|--------|-------------|
|
|
140
|
+
| `send(event)` | Publish event to all matching endpoints |
|
|
141
|
+
| `sendToTenant(tenantId, event)` | Publish to tenant-specific endpoints only |
|
|
142
|
+
|
|
143
|
+
### WebhookEndpointAdminService
|
|
144
|
+
|
|
145
|
+
| Method | Description |
|
|
146
|
+
|--------|-------------|
|
|
147
|
+
| `createEndpoint(dto)` | Register a new webhook endpoint (returns secret) |
|
|
148
|
+
| `listEndpoints(tenantId?)` | List all endpoints (secret excluded) |
|
|
149
|
+
| `getEndpoint(id)` | Get endpoint details (secret excluded) |
|
|
150
|
+
| `updateEndpoint(id, dto)` | Update endpoint URL, events, etc. |
|
|
151
|
+
| `deleteEndpoint(id)` | Delete an endpoint |
|
|
152
|
+
| `sendTestEvent(endpointId)` | Send a `webhook.test` ping event |
|
|
153
|
+
|
|
154
|
+
### WebhookDeliveryAdminService
|
|
155
|
+
|
|
156
|
+
| Method | Description |
|
|
157
|
+
|--------|-------------|
|
|
158
|
+
| `getDeliveryLogs(endpointId, filters?)` | Query delivery history |
|
|
159
|
+
| `retryDelivery(deliveryId)` | Manually retry a failed delivery |
|
|
160
|
+
|
|
161
|
+
### WebhookSigner
|
|
162
|
+
|
|
163
|
+
| Method | Description |
|
|
164
|
+
|--------|-------------|
|
|
165
|
+
| `sign(eventId, timestamp, body, secret)` | Generate Standard Webhooks signature headers |
|
|
166
|
+
| `verify(eventId, timestamp, body, secret, signature)` | Verify a webhook signature |
|
|
167
|
+
| `generateSecret()` | Generate a random base64 signing secret |
|
|
168
|
+
|
|
169
|
+
> **Deprecated:** `WebhookAdminService` is a facade that delegates to `WebhookEndpointAdminService` and `WebhookDeliveryAdminService`. It will be removed in v0.3.0.
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
| Option | Default | Description |
|
|
174
|
+
|--------|---------|-------------|
|
|
175
|
+
| `prisma` | — | PrismaClient instance (required unless all custom repos provided) |
|
|
176
|
+
| `delivery.timeout` | `10000` | HTTP request timeout (ms) |
|
|
177
|
+
| `delivery.maxRetries` | `5` | Maximum delivery attempts |
|
|
178
|
+
| `delivery.jitter` | `true` | Add random jitter to retry delays |
|
|
179
|
+
| `circuitBreaker.failureThreshold` | `5` | Consecutive failures before disabling endpoint |
|
|
180
|
+
| `circuitBreaker.cooldownMinutes` | `60` | Minutes before attempting recovery |
|
|
181
|
+
| `polling.interval` | `5000` | Delivery worker poll interval (ms) |
|
|
182
|
+
| `polling.batchSize` | `50` | Max deliveries per poll cycle |
|
|
183
|
+
| `polling.staleSendingMinutes` | `5` | Minutes before a stuck SENDING delivery is recovered |
|
|
184
|
+
| `allowPrivateUrls` | `false` | Allow private/internal URLs (dev/test only) |
|
|
185
|
+
|
|
186
|
+
### Custom adapters
|
|
187
|
+
|
|
188
|
+
Replace default Prisma or fetch implementations by providing custom ports:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
WebhookModule.forRoot({
|
|
192
|
+
prisma: prismaService,
|
|
193
|
+
httpClient: myCustomHttpClient, // implements WebhookHttpClient
|
|
194
|
+
eventRepository: myCustomEventRepo, // implements WebhookEventRepository
|
|
195
|
+
endpointRepository: myCustomEndpointRepo,// implements WebhookEndpointRepository
|
|
196
|
+
deliveryRepository: myCustomDeliveryRepo,// implements WebhookDeliveryRepository
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Async configuration
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
WebhookModule.forRootAsync({
|
|
204
|
+
imports: [ConfigModule],
|
|
205
|
+
useFactory: (config: ConfigService, prisma: PrismaService) => ({
|
|
206
|
+
prisma,
|
|
207
|
+
delivery: {
|
|
208
|
+
maxRetries: config.get('WEBHOOK_MAX_RETRIES', 5),
|
|
209
|
+
},
|
|
210
|
+
}),
|
|
211
|
+
inject: [ConfigService, PrismaService],
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Security
|
|
216
|
+
|
|
217
|
+
### Signing
|
|
218
|
+
|
|
219
|
+
All webhooks are signed with **HMAC-SHA256** using [Standard Webhooks](https://www.standardwebhooks.com/) headers:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
webhook-id: <event-uuid>
|
|
223
|
+
webhook-timestamp: <unix-seconds>
|
|
224
|
+
webhook-signature: v1,<base64-hmac-sha256>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Secret format:** Secrets must be valid base64 strings decoding to at least 16 bytes. Use `"auto"` for automatic generation.
|
|
228
|
+
|
|
229
|
+
### SSRF defense
|
|
230
|
+
|
|
231
|
+
- Endpoint URLs are validated at **registration** and at **every dispatch**
|
|
232
|
+
- Blocks: private IPs, loopback, link-local, cloud metadata (169.254.x), IPv4-mapped IPv6
|
|
233
|
+
- DNS resolution is checked to prevent rebinding attacks
|
|
234
|
+
- HTTP redirects are disabled (`redirect: 'manual'`)
|
|
235
|
+
- Use `allowPrivateUrls: true` for local development only
|
|
236
|
+
|
|
237
|
+
### Secret handling
|
|
238
|
+
|
|
239
|
+
- Signing secrets are excluded from read queries (`listEndpoints`, `getEndpoint`)
|
|
240
|
+
- Secrets are only returned on `createEndpoint` (initial provisioning)
|
|
241
|
+
- Delivery enrichment uses an internal path that does not expose secrets through admin APIs
|
|
242
|
+
|
|
243
|
+
## Webhook Payload Format
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"type": "order.created",
|
|
248
|
+
"data": {
|
|
249
|
+
"orderId": "ord_123",
|
|
250
|
+
"total": 99.99
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Architecture
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
┌─────────────┐ ┌──────────────────┐ ┌───────────────────┐
|
|
259
|
+
│ Your Service │────>│ WebhookService │────>│ PostgreSQL (tx) │
|
|
260
|
+
└─────────────┘ └──────────────────┘ └───────────────────┘
|
|
261
|
+
│
|
|
262
|
+
┌───────┴────────┐
|
|
263
|
+
│ DeliveryWorker │ (polls every N seconds)
|
|
264
|
+
└───────┬────────┘
|
|
265
|
+
│
|
|
266
|
+
┌─────────────┼─────────────┐
|
|
267
|
+
v v v
|
|
268
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
269
|
+
│Dispatcher│ │RetryPolicy│ │CircuitBkr│
|
|
270
|
+
└────┬─────┘ └──────────┘ └──────────┘
|
|
271
|
+
│
|
|
272
|
+
┌────┴─────┐
|
|
273
|
+
│HttpClient│──> customer endpoints
|
|
274
|
+
└──────────┘
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
All components depend on **port interfaces**, not concrete implementations. Default adapters use Prisma and Node.js fetch.
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { WebhookHttpClient } from '../ports/webhook-http-client';
|
|
2
|
+
import { DeliveryResult } from '../interfaces/webhook-delivery.interface';
|
|
3
|
+
export declare class FetchHttpClient implements WebhookHttpClient {
|
|
4
|
+
post(url: string, headers: Record<string, string>, body: string, timeout: number): Promise<DeliveryResult>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=fetch-http-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-http-client.d.ts","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAG1E,qBACa,eAAgB,YAAW,iBAAiB;IACjD,IAAI,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC;CAuC3B"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.FetchHttpClient = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const webhook_constants_1 = require("../webhook.constants");
|
|
12
|
+
let FetchHttpClient = class FetchHttpClient {
|
|
13
|
+
async post(url, headers, body, timeout) {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
16
|
+
const start = Date.now();
|
|
17
|
+
try {
|
|
18
|
+
const response = await fetch(url, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
'User-Agent': '@nestarc/webhook',
|
|
23
|
+
...headers,
|
|
24
|
+
},
|
|
25
|
+
body,
|
|
26
|
+
signal: controller.signal,
|
|
27
|
+
redirect: 'manual',
|
|
28
|
+
});
|
|
29
|
+
const latencyMs = Date.now() - start;
|
|
30
|
+
const responseBody = await response.text();
|
|
31
|
+
return {
|
|
32
|
+
success: response.status >= 200 && response.status < 300,
|
|
33
|
+
statusCode: response.status,
|
|
34
|
+
body: responseBody.slice(0, webhook_constants_1.RESPONSE_BODY_MAX_LENGTH),
|
|
35
|
+
latencyMs,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const latencyMs = Date.now() - start;
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
latencyMs,
|
|
44
|
+
error: message,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
clearTimeout(timeoutId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
exports.FetchHttpClient = FetchHttpClient;
|
|
53
|
+
exports.FetchHttpClient = FetchHttpClient = __decorate([
|
|
54
|
+
(0, common_1.Injectable)()
|
|
55
|
+
], FetchHttpClient);
|
|
56
|
+
//# sourceMappingURL=fetch-http-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-http-client.js","sourceRoot":"","sources":["../../src/adapters/fetch-http-client.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAG5C,4DAAgE;AAGzD,IAAM,eAAe,GAArB,MAAM,eAAe;IAC1B,KAAK,CAAC,IAAI,CACR,GAAW,EACX,OAA+B,EAC/B,IAAY,EACZ,OAAe;QAEf,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,YAAY,EAAE,kBAAkB;oBAChC,GAAG,OAAO;iBACX;gBACD,IAAI;gBACJ,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,QAAQ,EAAE,QAAQ;aACnB,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACrC,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAE3C,OAAO;gBACL,OAAO,EAAE,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG;gBACxD,UAAU,EAAE,QAAQ,CAAC,MAAM;gBAC3B,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,4CAAwB,CAAC;gBACrD,SAAS;aACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACrC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,SAAS;gBACT,KAAK,EAAE,OAAO;aACf,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;CACF,CAAA;AA7CY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;GACA,eAAe,CA6C3B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PendingDelivery, WebhookDeliveryRepository } from '../ports/webhook-delivery.repository';
|
|
2
|
+
import { DeliveryLogFilters, DeliveryRecord, DeliveryResult } from '../interfaces/webhook-delivery.interface';
|
|
3
|
+
export declare class PrismaDeliveryRepository implements WebhookDeliveryRepository {
|
|
4
|
+
private readonly prisma;
|
|
5
|
+
constructor(prisma: any);
|
|
6
|
+
createDeliveriesInTransaction(tx: any, eventId: string, endpointIds: string[], maxAttempts: number): Promise<void>;
|
|
7
|
+
runInTransaction<T>(fn: (tx: unknown) => Promise<T>): Promise<T>;
|
|
8
|
+
claimPendingDeliveries(batchSize: number): Promise<PendingDelivery[]>;
|
|
9
|
+
enrichDeliveries(deliveryIds: string[]): Promise<PendingDelivery[]>;
|
|
10
|
+
markSent(deliveryId: string, attempts: number, result: DeliveryResult): Promise<void>;
|
|
11
|
+
markFailed(deliveryId: string, attempts: number, result: DeliveryResult): Promise<void>;
|
|
12
|
+
markRetry(deliveryId: string, attempts: number, nextAt: Date, result: DeliveryResult): Promise<void>;
|
|
13
|
+
recoverStaleSending(stalenessMinutes: number): Promise<number>;
|
|
14
|
+
getDeliveryLogs(endpointId: string, filters?: DeliveryLogFilters): Promise<DeliveryRecord[]>;
|
|
15
|
+
retryDelivery(deliveryId: string): Promise<boolean>;
|
|
16
|
+
createTestDelivery(eventId: string, endpointId: string): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=prisma-delivery.repository.d.ts.map
|
|
@@ -0,0 +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;AAElD,qBACa,wBAAyB,YAAW,yBAAyB;IAC5D,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,GAAG;IAElC,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;IAYnE,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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PrismaDeliveryRepository = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
let PrismaDeliveryRepository = class PrismaDeliveryRepository {
|
|
15
|
+
prisma;
|
|
16
|
+
constructor(prisma) {
|
|
17
|
+
this.prisma = prisma;
|
|
18
|
+
}
|
|
19
|
+
async createDeliveriesInTransaction(tx, eventId, endpointIds, maxAttempts) {
|
|
20
|
+
await tx.$executeRawUnsafe(`INSERT INTO webhook_deliveries (event_id, endpoint_id, status, attempts, max_attempts, next_attempt_at)
|
|
21
|
+
SELECT $1::uuid, unnest($2::uuid[]), 'PENDING', 0, $3, NOW()`, eventId, endpointIds, maxAttempts);
|
|
22
|
+
}
|
|
23
|
+
async runInTransaction(fn) {
|
|
24
|
+
return this.prisma.$transaction(fn);
|
|
25
|
+
}
|
|
26
|
+
async claimPendingDeliveries(batchSize) {
|
|
27
|
+
return this.prisma.$queryRaw `
|
|
28
|
+
UPDATE webhook_deliveries
|
|
29
|
+
SET status = 'SENDING', claimed_at = NOW()
|
|
30
|
+
WHERE id IN (
|
|
31
|
+
SELECT d.id
|
|
32
|
+
FROM webhook_deliveries d
|
|
33
|
+
WHERE d.status = 'PENDING'
|
|
34
|
+
AND d.next_attempt_at <= NOW()
|
|
35
|
+
ORDER BY d.next_attempt_at ASC
|
|
36
|
+
LIMIT ${batchSize}
|
|
37
|
+
FOR UPDATE SKIP LOCKED
|
|
38
|
+
)
|
|
39
|
+
RETURNING
|
|
40
|
+
webhook_deliveries.id,
|
|
41
|
+
webhook_deliveries.event_id,
|
|
42
|
+
webhook_deliveries.endpoint_id,
|
|
43
|
+
webhook_deliveries.attempts,
|
|
44
|
+
webhook_deliveries.max_attempts`;
|
|
45
|
+
}
|
|
46
|
+
async enrichDeliveries(deliveryIds) {
|
|
47
|
+
return this.prisma.$queryRaw `
|
|
48
|
+
SELECT
|
|
49
|
+
d.id, d.event_id, d.endpoint_id, d.attempts, d.max_attempts,
|
|
50
|
+
e.url, e.secret,
|
|
51
|
+
ev.event_type, ev.payload
|
|
52
|
+
FROM webhook_deliveries d
|
|
53
|
+
JOIN webhook_endpoints e ON e.id = d.endpoint_id
|
|
54
|
+
JOIN webhook_events ev ON ev.id = d.event_id
|
|
55
|
+
WHERE d.id = ANY(${deliveryIds}::uuid[])`;
|
|
56
|
+
}
|
|
57
|
+
async markSent(deliveryId, attempts, result) {
|
|
58
|
+
await this.prisma.$executeRaw `
|
|
59
|
+
UPDATE webhook_deliveries
|
|
60
|
+
SET status = 'SENT', attempts = ${attempts},
|
|
61
|
+
last_attempt_at = NOW(), completed_at = NOW(),
|
|
62
|
+
response_status = ${result.statusCode ?? null},
|
|
63
|
+
response_body = ${result.body ?? null},
|
|
64
|
+
latency_ms = ${result.latencyMs}
|
|
65
|
+
WHERE id = ${deliveryId}::uuid`;
|
|
66
|
+
}
|
|
67
|
+
async markFailed(deliveryId, attempts, result) {
|
|
68
|
+
await this.prisma.$executeRaw `
|
|
69
|
+
UPDATE webhook_deliveries
|
|
70
|
+
SET status = 'FAILED', attempts = ${attempts},
|
|
71
|
+
last_attempt_at = NOW(), completed_at = NOW(),
|
|
72
|
+
response_status = ${result.statusCode ?? null},
|
|
73
|
+
response_body = ${result.body ?? null},
|
|
74
|
+
latency_ms = ${result.latencyMs},
|
|
75
|
+
last_error = ${result.error ?? null}
|
|
76
|
+
WHERE id = ${deliveryId}::uuid`;
|
|
77
|
+
}
|
|
78
|
+
async markRetry(deliveryId, attempts, nextAt, result) {
|
|
79
|
+
await this.prisma.$executeRaw `
|
|
80
|
+
UPDATE webhook_deliveries
|
|
81
|
+
SET status = 'PENDING', attempts = ${attempts},
|
|
82
|
+
last_attempt_at = NOW(), next_attempt_at = ${nextAt},
|
|
83
|
+
response_status = ${result.statusCode ?? null},
|
|
84
|
+
response_body = ${result.body ?? null},
|
|
85
|
+
latency_ms = ${result.latencyMs},
|
|
86
|
+
last_error = ${result.error ?? null}
|
|
87
|
+
WHERE id = ${deliveryId}::uuid`;
|
|
88
|
+
}
|
|
89
|
+
async recoverStaleSending(stalenessMinutes) {
|
|
90
|
+
const interval = `${stalenessMinutes} minutes`;
|
|
91
|
+
const recovered = await this.prisma.$queryRaw `
|
|
92
|
+
UPDATE webhook_deliveries
|
|
93
|
+
SET status = 'PENDING', claimed_at = NULL
|
|
94
|
+
WHERE status = 'SENDING'
|
|
95
|
+
AND claimed_at IS NOT NULL
|
|
96
|
+
AND claimed_at + ${interval}::interval < NOW()
|
|
97
|
+
RETURNING id`;
|
|
98
|
+
return recovered.length;
|
|
99
|
+
}
|
|
100
|
+
async getDeliveryLogs(endpointId, filters) {
|
|
101
|
+
const conditions = ['d.endpoint_id = $1::uuid'];
|
|
102
|
+
const values = [endpointId];
|
|
103
|
+
let paramIndex = 2;
|
|
104
|
+
if (filters?.status) {
|
|
105
|
+
conditions.push(`d.status = $${paramIndex++}`);
|
|
106
|
+
values.push(filters.status);
|
|
107
|
+
}
|
|
108
|
+
if (filters?.eventType) {
|
|
109
|
+
conditions.push(`ev.event_type = $${paramIndex++}`);
|
|
110
|
+
values.push(filters.eventType);
|
|
111
|
+
}
|
|
112
|
+
if (filters?.since) {
|
|
113
|
+
conditions.push(`d.last_attempt_at >= $${paramIndex++}`);
|
|
114
|
+
values.push(filters.since);
|
|
115
|
+
}
|
|
116
|
+
if (filters?.until) {
|
|
117
|
+
conditions.push(`d.last_attempt_at <= $${paramIndex++}`);
|
|
118
|
+
values.push(filters.until);
|
|
119
|
+
}
|
|
120
|
+
const limit = filters?.limit ?? 50;
|
|
121
|
+
const offset = filters?.offset ?? 0;
|
|
122
|
+
const query = `
|
|
123
|
+
SELECT d.id, d.status, d.attempts,
|
|
124
|
+
d.event_id AS "eventId",
|
|
125
|
+
d.endpoint_id AS "endpointId",
|
|
126
|
+
d.max_attempts AS "maxAttempts",
|
|
127
|
+
d.next_attempt_at AS "nextAttemptAt",
|
|
128
|
+
d.last_attempt_at AS "lastAttemptAt",
|
|
129
|
+
d.completed_at AS "completedAt",
|
|
130
|
+
d.response_status AS "responseStatus",
|
|
131
|
+
d.response_body AS "responseBody",
|
|
132
|
+
d.latency_ms AS "latencyMs",
|
|
133
|
+
d.last_error AS "lastError"
|
|
134
|
+
FROM webhook_deliveries d
|
|
135
|
+
JOIN webhook_events ev ON ev.id = d.event_id
|
|
136
|
+
WHERE ${conditions.join(' AND ')}
|
|
137
|
+
ORDER BY d.last_attempt_at DESC NULLS LAST
|
|
138
|
+
LIMIT $${paramIndex++}
|
|
139
|
+
OFFSET $${paramIndex}`;
|
|
140
|
+
values.push(limit, offset);
|
|
141
|
+
const results = await this.prisma.$queryRawUnsafe(query, ...values);
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
async retryDelivery(deliveryId) {
|
|
145
|
+
const result = await this.prisma.$executeRaw `
|
|
146
|
+
UPDATE webhook_deliveries
|
|
147
|
+
SET status = 'PENDING', next_attempt_at = NOW()
|
|
148
|
+
WHERE id = ${deliveryId}::uuid AND status = 'FAILED'`;
|
|
149
|
+
return result > 0;
|
|
150
|
+
}
|
|
151
|
+
async createTestDelivery(eventId, endpointId) {
|
|
152
|
+
await this.prisma.$executeRaw `
|
|
153
|
+
INSERT INTO webhook_deliveries (event_id, endpoint_id, status, max_attempts, next_attempt_at)
|
|
154
|
+
VALUES (${eventId}::uuid, ${endpointId}::uuid, 'PENDING', 1, NOW())`;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
exports.PrismaDeliveryRepository = PrismaDeliveryRepository;
|
|
158
|
+
exports.PrismaDeliveryRepository = PrismaDeliveryRepository = __decorate([
|
|
159
|
+
(0, common_1.Injectable)(),
|
|
160
|
+
__metadata("design:paramtypes", [Object])
|
|
161
|
+
], PrismaDeliveryRepository);
|
|
162
|
+
//# sourceMappingURL=prisma-delivery.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-delivery.repository.js","sourceRoot":"","sources":["../../src/adapters/prisma-delivery.repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { WebhookEndpointRepository } from '../ports/webhook-endpoint.repository';
|
|
2
|
+
import { EndpointRecord, EndpointRecordWithSecret, UpdateEndpointDto } from '../interfaces/webhook-endpoint.interface';
|
|
3
|
+
export declare class PrismaEndpointRepository implements WebhookEndpointRepository {
|
|
4
|
+
private readonly prisma;
|
|
5
|
+
constructor(prisma: any);
|
|
6
|
+
findMatchingEndpoints(eventType: string, tenantId: string | undefined): Promise<EndpointRecord[]>;
|
|
7
|
+
findMatchingEndpointsInTransaction(tx: any, eventType: string, tenantId: string | undefined): Promise<EndpointRecord[]>;
|
|
8
|
+
createEndpoint(url: string, secret: string, events: string[], description: string | null, metadata: Record<string, unknown> | null, tenantId: string | null): Promise<EndpointRecordWithSecret>;
|
|
9
|
+
getEndpoint(id: string): Promise<EndpointRecord | null>;
|
|
10
|
+
listEndpoints(tenantId?: string): Promise<EndpointRecord[]>;
|
|
11
|
+
updateEndpoint(id: string, dto: UpdateEndpointDto): Promise<EndpointRecord | null>;
|
|
12
|
+
deleteEndpoint(id: string): Promise<boolean>;
|
|
13
|
+
resetFailures(endpointId: string): Promise<void>;
|
|
14
|
+
incrementFailures(endpointId: string): Promise<number>;
|
|
15
|
+
disableEndpoint(endpointId: string, reason: string): Promise<void>;
|
|
16
|
+
recoverEligibleEndpoints(cooldownMinutes: number): Promise<number>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=prisma-endpoint.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-endpoint.repository.d.ts","sourceRoot":"","sources":["../../src/adapters/prisma-endpoint.repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EAClB,MAAM,0CAA0C,CAAC;AAsBlD,qBACa,wBAAyB,YAAW,yBAAyB;IAC5D,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,GAAG;IAElC,qBAAqB,CACzB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC;IAoBtB,kCAAkC,CACtC,EAAE,EAAE,GAAG,EACP,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,cAAc,EAAE,CAAC;IAoBtB,cAAc,CAClB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EAAE,EAChB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACxC,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,OAAO,CAAC,wBAAwB,CAAC;IAe9B,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAQvD,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAe3D,cAAc,CAClB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAqC3B,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhD,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAStD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,wBAAwB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAYzE"}
|