@endora-commerce/mod-webhooks 0.100.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 +59 -0
- package/dist/admin/index.d.ts +34 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +45 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/WebhooksPage.d.ts +3 -0
- package/dist/admin/pages/WebhooksPage.d.ts.map +1 -0
- package/dist/admin/pages/WebhooksPage.js +220 -0
- package/dist/admin/pages/WebhooksPage.js.map +1 -0
- package/dist/backend/entities/webhook-delivery.entity.d.ts +29 -0
- package/dist/backend/entities/webhook-delivery.entity.d.ts.map +1 -0
- package/dist/backend/entities/webhook-delivery.entity.js +108 -0
- package/dist/backend/entities/webhook-delivery.entity.js.map +1 -0
- package/dist/backend/entities/webhook.entity.d.ts +33 -0
- package/dist/backend/entities/webhook.entity.d.ts.map +1 -0
- package/dist/backend/entities/webhook.entity.js +91 -0
- package/dist/backend/entities/webhook.entity.js.map +1 -0
- package/dist/backend/index.d.ts +85 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +129 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.d.ts +26 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +84 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/services/event-bridge.d.ts +54 -0
- package/dist/backend/services/event-bridge.d.ts.map +1 -0
- package/dist/backend/services/event-bridge.js +43 -0
- package/dist/backend/services/event-bridge.js.map +1 -0
- package/dist/backend/services/webhook-delivery-worker.d.ts +34 -0
- package/dist/backend/services/webhook-delivery-worker.d.ts.map +1 -0
- package/dist/backend/services/webhook-delivery-worker.js +70 -0
- package/dist/backend/services/webhook-delivery-worker.js.map +1 -0
- package/dist/backend/services/webhook-queue.d.ts +24 -0
- package/dist/backend/services/webhook-queue.d.ts.map +1 -0
- package/dist/backend/services/webhook-queue.js +31 -0
- package/dist/backend/services/webhook-queue.js.map +1 -0
- package/dist/backend/services/webhook-service.d.ts +82 -0
- package/dist/backend/services/webhook-service.d.ts.map +1 -0
- package/dist/backend/services/webhook-service.js +176 -0
- package/dist/backend/services/webhook-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +122 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260425T091359_webhooks_us7_init.d.ts +22 -0
- package/dist/migrations/20260425T091359_webhooks_us7_init.d.ts.map +1 -0
- package/dist/migrations/20260425T091359_webhooks_us7_init.js +86 -0
- package/dist/migrations/20260425T091359_webhooks_us7_init.js.map +1 -0
- package/dist/migrations/20260724T203140_webhooks_org_filter.d.ts +17 -0
- package/dist/migrations/20260724T203140_webhooks_org_filter.d.ts.map +1 -0
- package/dist/migrations/20260724T203140_webhooks_org_filter.js +30 -0
- package/dist/migrations/20260724T203140_webhooks_org_filter.js.map +1 -0
- package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.d.ts +28 -0
- package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.d.ts.map +1 -0
- package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.js +49 -0
- package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.js.map +1 -0
- package/dist/migrations/index.d.ts +29 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +33 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/webhooks.md +50 -0
- package/i18n/en.json +6 -0
- package/i18n/pl.json +6 -0
- package/package.json +97 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createHmac } from 'crypto';
|
|
2
|
+
export function createDeliveryProcessor(deps = {}) {
|
|
3
|
+
const { fetchFn, timeoutMs = 10_000, onFailure, recordDelivery } = deps;
|
|
4
|
+
const record = async (input) => {
|
|
5
|
+
if (!recordDelivery)
|
|
6
|
+
return;
|
|
7
|
+
try {
|
|
8
|
+
await recordDelivery(input);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Bookkeeping is best-effort — never let it mask the delivery outcome.
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
return async function process(job) {
|
|
15
|
+
const doFetch = fetchFn ?? globalThis.fetch;
|
|
16
|
+
const { webhookId, url, secret, payload, eventId, eventType } = job.data;
|
|
17
|
+
const body = JSON.stringify(payload);
|
|
18
|
+
const signature = createHmac('sha256', secret).update(body, 'utf8').digest('hex');
|
|
19
|
+
const attemptCount = job.attemptsMade + 1;
|
|
20
|
+
const maxAttempts = job.opts?.attempts ?? 1;
|
|
21
|
+
const abort = new AbortController();
|
|
22
|
+
const timer = setTimeout(() => abort.abort(), timeoutMs);
|
|
23
|
+
let responseStatus;
|
|
24
|
+
try {
|
|
25
|
+
const response = await doFetch(url, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
'X-Webhook-Event-Id': eventId,
|
|
30
|
+
'X-Webhook-Event-Type': eventType,
|
|
31
|
+
'X-Webhook-Signature-256': signature,
|
|
32
|
+
'X-Webhook-Attempt': String(attemptCount),
|
|
33
|
+
},
|
|
34
|
+
body,
|
|
35
|
+
signal: abort.signal,
|
|
36
|
+
});
|
|
37
|
+
responseStatus = response.status;
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
throw new Error(`webhook receiver returned ${response.status}`);
|
|
40
|
+
}
|
|
41
|
+
await record({
|
|
42
|
+
webhookId,
|
|
43
|
+
eventId,
|
|
44
|
+
eventType,
|
|
45
|
+
payload,
|
|
46
|
+
status: 'succeeded',
|
|
47
|
+
attemptCount,
|
|
48
|
+
lastResponseStatus: response.status,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
onFailure?.(err, job);
|
|
53
|
+
await record({
|
|
54
|
+
webhookId,
|
|
55
|
+
eventId,
|
|
56
|
+
eventType,
|
|
57
|
+
payload,
|
|
58
|
+
status: attemptCount >= maxAttempts ? 'dead_lettered' : 'failed',
|
|
59
|
+
attemptCount,
|
|
60
|
+
lastError: err instanceof Error ? err.message : String(err),
|
|
61
|
+
...(responseStatus !== undefined ? { lastResponseStatus: responseStatus } : {}),
|
|
62
|
+
});
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=webhook-delivery-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-delivery-worker.js","sourceRoot":"","sources":["../../../src/backend/services/webhook-delivery-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Queue, Worker, type Processor, type QueueOptions, type WorkerOptions } from 'bullmq';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
/**
|
|
4
|
+
* BullMQ queue manager for outbound webhook delivery (R-13).
|
|
5
|
+
*
|
|
6
|
+
* - Single queue `webhook.deliver` with exponential-backoff retries.
|
|
7
|
+
* - Payloads are opaque; the worker signs + POSTs them.
|
|
8
|
+
* - Dead-letter is handled by BullMQ's `failed` job state — consumable by the admin
|
|
9
|
+
* panel's failed-deliveries view.
|
|
10
|
+
*/
|
|
11
|
+
export interface WebhookJobData {
|
|
12
|
+
webhookId: string;
|
|
13
|
+
eventId: string;
|
|
14
|
+
eventType: string;
|
|
15
|
+
payload: unknown;
|
|
16
|
+
/** Receiver URL at enqueue time (copied so URL changes don't retroactively affect queued jobs). */
|
|
17
|
+
url: string;
|
|
18
|
+
/** HMAC signing secret at enqueue time. */
|
|
19
|
+
secret: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const WEBHOOK_QUEUE_NAME = "webhook.deliver";
|
|
22
|
+
export declare function createWebhookQueue(redis: Redis, overrides?: Partial<QueueOptions>): Queue<WebhookJobData>;
|
|
23
|
+
export declare function createWebhookWorker(redis: Redis, processor: Processor<WebhookJobData>, overrides?: Partial<WorkerOptions>): Worker<WebhookJobData>;
|
|
24
|
+
//# sourceMappingURL=webhook-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-queue.d.ts","sourceRoot":"","sources":["../../../src/backend/services/webhook-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE9F,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC;;;;;;;GAOG;AAEH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,mGAAmG;IACnG,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAKpD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,cAAc,CAAC,CAYzG;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,EACpC,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GACjC,MAAM,CAAC,cAAc,CAAC,CAexB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Queue, Worker } from 'bullmq';
|
|
2
|
+
import { enterSystemScope } from '@endora-commerce/platform/kernel';
|
|
3
|
+
export const WEBHOOK_QUEUE_NAME = 'webhook.deliver';
|
|
4
|
+
const DEFAULT_ATTEMPTS = 8;
|
|
5
|
+
const DEFAULT_BACKOFF = { type: 'exponential', delay: 1_000 };
|
|
6
|
+
export function createWebhookQueue(redis, overrides) {
|
|
7
|
+
const options = {
|
|
8
|
+
connection: redis,
|
|
9
|
+
defaultJobOptions: {
|
|
10
|
+
attempts: DEFAULT_ATTEMPTS,
|
|
11
|
+
backoff: DEFAULT_BACKOFF,
|
|
12
|
+
removeOnComplete: { count: 500 },
|
|
13
|
+
removeOnFail: { count: 5_000 },
|
|
14
|
+
},
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
return new Queue(WEBHOOK_QUEUE_NAME, options);
|
|
18
|
+
}
|
|
19
|
+
export function createWebhookWorker(redis, processor, overrides) {
|
|
20
|
+
const options = {
|
|
21
|
+
connection: redis,
|
|
22
|
+
concurrency: 8,
|
|
23
|
+
...overrides,
|
|
24
|
+
};
|
|
25
|
+
// Feature 072 (T033) — a delivery job runs detached, so it establishes its
|
|
26
|
+
// own scope here. Before this it established NOTHING: it survived only
|
|
27
|
+
// because `Webhook` and `WebhookDelivery` carry no automatic tenant filter,
|
|
28
|
+
// which is a fact about their classification, not a guarantee.
|
|
29
|
+
return new Worker(WEBHOOK_QUEUE_NAME, (job) => enterSystemScope('webhooks: deliver', () => processor(job)), options);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=webhook-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-queue.js","sourceRoot":"","sources":["../../../src/backend/services/webhook-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAyD,MAAM,QAAQ,CAAC;AAC9F,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAuBpE,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEpD,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,eAAe,GAAG,EAAE,IAAI,EAAE,aAAsB,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAEvE,MAAM,UAAU,kBAAkB,CAAC,KAAY,EAAE,SAAiC;IAChF,MAAM,OAAO,GAAiB;QAC5B,UAAU,EAAE,KAAK;QACjB,iBAAiB,EAAE;YACjB,QAAQ,EAAE,gBAAgB;YAC1B,OAAO,EAAE,eAAe;YACxB,gBAAgB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;YAChC,YAAY,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;SAC/B;QACD,GAAG,SAAS;KACb,CAAC;IACF,OAAO,IAAI,KAAK,CAAiB,kBAAkB,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,KAAY,EACZ,SAAoC,EACpC,SAAkC;IAElC,MAAM,OAAO,GAAkB;QAC7B,UAAU,EAAE,KAAK;QACjB,WAAW,EAAE,CAAC;QACd,GAAG,SAAS;KACb,CAAC;IACF,2EAA2E;IAC3E,uEAAuE;IACvE,4EAA4E;IAC5E,+DAA+D;IAC/D,OAAO,IAAI,MAAM,CACf,kBAAkB,EAClB,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EACpE,OAAO,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { Webhook } from '../entities/webhook.entity.js';
|
|
3
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import { WebhookDelivery } from '../entities/webhook-delivery.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* WebhookService (T229) — admin CRUD over Webhook subscriptions plus
|
|
7
|
+
* read-side queries over the WebhookDelivery audit table.
|
|
8
|
+
*
|
|
9
|
+
* Each created Webhook gets a freshly minted 64-char hex secret; the worker
|
|
10
|
+
* uses the same secret to HMAC-sign each outbound POST.
|
|
11
|
+
*/
|
|
12
|
+
export declare class WebhookService {
|
|
13
|
+
#private;
|
|
14
|
+
private readonly emFactory;
|
|
15
|
+
private readonly auditLog?;
|
|
16
|
+
constructor(emFactory: () => EntityManager, auditLog?: AuditPort | undefined);
|
|
17
|
+
list(): Promise<Webhook[]>;
|
|
18
|
+
getById(id: string): Promise<Webhook>;
|
|
19
|
+
create(input: {
|
|
20
|
+
name: string;
|
|
21
|
+
url: string;
|
|
22
|
+
eventTypes: string[];
|
|
23
|
+
/** Feature 062 — optional org binding; null/omitted = platform-wide. */
|
|
24
|
+
organizationId?: string | null;
|
|
25
|
+
createdByAdminUserId?: string;
|
|
26
|
+
}): Promise<Webhook>;
|
|
27
|
+
update(id: string, patch: {
|
|
28
|
+
name?: string | undefined;
|
|
29
|
+
url?: string | undefined;
|
|
30
|
+
eventTypes?: string[] | undefined;
|
|
31
|
+
status?: 'active' | 'paused' | undefined;
|
|
32
|
+
/** Feature 062 — set to bind, null to make platform-wide. */
|
|
33
|
+
organizationId?: string | null | undefined;
|
|
34
|
+
}): Promise<Webhook>;
|
|
35
|
+
remove(id: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Re-queue a failed or dead-lettered delivery: copy the source delivery into
|
|
38
|
+
* a fresh `pending` row referencing the same event/payload. The actual
|
|
39
|
+
* outbound POST is performed by the BullMQ worker once the job is enqueued
|
|
40
|
+
* by the production composition root; the new row is the audit trail.
|
|
41
|
+
*
|
|
42
|
+
* Only `failed` and `dead_lettered` rows can be replayed — calling this on
|
|
43
|
+
* an already-pending or successful row is a no-op signalled via 409.
|
|
44
|
+
*/
|
|
45
|
+
replay(deliveryId: string): Promise<WebhookDelivery>;
|
|
46
|
+
listDeliveries(filter?: {
|
|
47
|
+
webhookId?: string;
|
|
48
|
+
status?: 'pending' | 'in_flight' | 'succeeded' | 'failed' | 'dead_lettered';
|
|
49
|
+
limit?: number;
|
|
50
|
+
}): Promise<WebhookDelivery[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Look up active subscriptions for a given event type — used by the
|
|
53
|
+
* event-bridge to decide which receivers should get a delivery.
|
|
54
|
+
*
|
|
55
|
+
* Feature 062 (contracts/order-webhooks.md §2): `organizationId` is the
|
|
56
|
+
* value extracted from the event payload. Platform-wide subscriptions
|
|
57
|
+
* (`organizationId` NULL) always match; org-bound subscriptions match only
|
|
58
|
+
* their own organization's events and never an event without one
|
|
59
|
+
* (fail closed — Principle XI).
|
|
60
|
+
*/
|
|
61
|
+
findActiveByEventType(eventType: string, organizationId: string | null): Promise<Array<{
|
|
62
|
+
webhookId: string;
|
|
63
|
+
url: string;
|
|
64
|
+
secret: string;
|
|
65
|
+
}>>;
|
|
66
|
+
/**
|
|
67
|
+
* Records an attempted delivery. The worker (webhook-delivery-worker.ts)
|
|
68
|
+
* is the canonical caller via the BullMQ pipeline; this method also lets
|
|
69
|
+
* tests + admin replay flows insert audit rows directly.
|
|
70
|
+
*/
|
|
71
|
+
recordDelivery(input: {
|
|
72
|
+
webhookId: string;
|
|
73
|
+
eventId: string;
|
|
74
|
+
eventType: string;
|
|
75
|
+
payload: unknown;
|
|
76
|
+
status: WebhookDelivery['status'];
|
|
77
|
+
attemptCount: number;
|
|
78
|
+
lastResponseStatus?: number;
|
|
79
|
+
lastError?: string;
|
|
80
|
+
}): Promise<WebhookDelivery>;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=webhook-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/webhook-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAGzE;;;;;;GAMG;AACH,qBAAa,cAAc;;IAEvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBADT,SAAS,EAAE,MAAM,aAAa,EAC9B,QAAQ,CAAC,EAAE,SAAS,YAAA;IASjC,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAK1B,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOrC,MAAM,CAAC,KAAK,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,wEAAwE;QACxE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,oBAAoB,CAAC,EAAE,MAAM,CAAC;KAC/B,GAAG,OAAO,CAAC,OAAO,CAAC;IAsBd,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAClC,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;QACzC,6DAA6D;QAC7D,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;KAC5C,GACA,OAAO,CAAC,OAAO,CAAC;IAkBb,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvC;;;;;;;;OAQG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA2BpD,cAAc,CAAC,MAAM,GAAE;QAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,eAAe,CAAC;QAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;KACX,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAWnC;;;;;;;;;OASG;IACG,qBAAqB,CACzB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IASrE;;;;OAIG;IACG,cAAc,CAAC,KAAK,EAAE;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClC,YAAY,EAAE,MAAM,CAAC;QACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,eAAe,CAAC;CAmB7B"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
3
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
4
|
+
import { Webhook } from '../entities/webhook.entity.js';
|
|
5
|
+
import { recordAuditFromContext } from '@endora-commerce/platform/commands';
|
|
6
|
+
import { WebhookDelivery } from '../entities/webhook-delivery.entity.js';
|
|
7
|
+
import { subscriptionReceivesOrganization } from './event-bridge.js';
|
|
8
|
+
/**
|
|
9
|
+
* WebhookService (T229) — admin CRUD over Webhook subscriptions plus
|
|
10
|
+
* read-side queries over the WebhookDelivery audit table.
|
|
11
|
+
*
|
|
12
|
+
* Each created Webhook gets a freshly minted 64-char hex secret; the worker
|
|
13
|
+
* uses the same secret to HMAC-sign each outbound POST.
|
|
14
|
+
*/
|
|
15
|
+
export class WebhookService {
|
|
16
|
+
emFactory;
|
|
17
|
+
auditLog;
|
|
18
|
+
constructor(emFactory, auditLog) {
|
|
19
|
+
this.emFactory = emFactory;
|
|
20
|
+
this.auditLog = auditLog;
|
|
21
|
+
}
|
|
22
|
+
#audit(em, action, objectId, stateBefore, stateAfter) {
|
|
23
|
+
if (this.auditLog) {
|
|
24
|
+
recordAuditFromContext(this.auditLog, em, { action, objectType: 'webhook', objectId, stateBefore, stateAfter });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async list() {
|
|
28
|
+
const em = this.emFactory();
|
|
29
|
+
return em.find(Webhook, {}, { orderBy: { createdAt: 'desc' } });
|
|
30
|
+
}
|
|
31
|
+
async getById(id) {
|
|
32
|
+
const em = this.emFactory();
|
|
33
|
+
const webhook = await em.findOne(Webhook, { id });
|
|
34
|
+
if (!webhook)
|
|
35
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Webhook not found.');
|
|
36
|
+
return webhook;
|
|
37
|
+
}
|
|
38
|
+
async create(input) {
|
|
39
|
+
const em = this.emFactory();
|
|
40
|
+
const webhook = em.create(Webhook, {
|
|
41
|
+
name: input.name,
|
|
42
|
+
url: input.url,
|
|
43
|
+
eventTypes: input.eventTypes,
|
|
44
|
+
secret: randomBytes(32).toString('hex'),
|
|
45
|
+
organizationId: input.organizationId ?? null,
|
|
46
|
+
...(input.createdByAdminUserId !== undefined
|
|
47
|
+
? { createdByAdminUserId: input.createdByAdminUserId }
|
|
48
|
+
: {}),
|
|
49
|
+
});
|
|
50
|
+
em.persist(webhook);
|
|
51
|
+
this.#audit(em, 'webhook.create', webhook.id, null, {
|
|
52
|
+
name: webhook.name,
|
|
53
|
+
url: webhook.url,
|
|
54
|
+
organizationId: webhook.organizationId ?? null,
|
|
55
|
+
});
|
|
56
|
+
await em.flush();
|
|
57
|
+
return webhook;
|
|
58
|
+
}
|
|
59
|
+
async update(id, patch) {
|
|
60
|
+
const em = this.emFactory();
|
|
61
|
+
const webhook = await em.findOne(Webhook, { id });
|
|
62
|
+
if (!webhook)
|
|
63
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Webhook not found.');
|
|
64
|
+
if (patch.name !== undefined)
|
|
65
|
+
webhook.name = patch.name;
|
|
66
|
+
if (patch.url !== undefined)
|
|
67
|
+
webhook.url = patch.url;
|
|
68
|
+
if (patch.eventTypes !== undefined)
|
|
69
|
+
webhook.eventTypes = patch.eventTypes;
|
|
70
|
+
if (patch.status !== undefined)
|
|
71
|
+
webhook.status = patch.status;
|
|
72
|
+
if (patch.organizationId !== undefined)
|
|
73
|
+
webhook.organizationId = patch.organizationId;
|
|
74
|
+
this.#audit(em, 'webhook.update', webhook.id, null, {
|
|
75
|
+
name: webhook.name,
|
|
76
|
+
status: webhook.status,
|
|
77
|
+
organizationId: webhook.organizationId ?? null,
|
|
78
|
+
});
|
|
79
|
+
await em.flush();
|
|
80
|
+
return webhook;
|
|
81
|
+
}
|
|
82
|
+
async remove(id) {
|
|
83
|
+
const em = this.emFactory();
|
|
84
|
+
const webhook = await em.findOne(Webhook, { id });
|
|
85
|
+
if (!webhook)
|
|
86
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Webhook not found.');
|
|
87
|
+
this.#audit(em, 'webhook.delete', webhook.id, { name: webhook.name }, null);
|
|
88
|
+
await em.removeAndFlush(webhook);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Re-queue a failed or dead-lettered delivery: copy the source delivery into
|
|
92
|
+
* a fresh `pending` row referencing the same event/payload. The actual
|
|
93
|
+
* outbound POST is performed by the BullMQ worker once the job is enqueued
|
|
94
|
+
* by the production composition root; the new row is the audit trail.
|
|
95
|
+
*
|
|
96
|
+
* Only `failed` and `dead_lettered` rows can be replayed — calling this on
|
|
97
|
+
* an already-pending or successful row is a no-op signalled via 409.
|
|
98
|
+
*/
|
|
99
|
+
async replay(deliveryId) {
|
|
100
|
+
// command-coverage-ignore: delivery execution/bookkeeping — the config write
|
|
101
|
+
// is audited separately; this is provider dispatch state.
|
|
102
|
+
const em = this.emFactory();
|
|
103
|
+
const source = await em.findOne(WebhookDelivery, { id: deliveryId });
|
|
104
|
+
if (!source) {
|
|
105
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Webhook delivery not found.');
|
|
106
|
+
}
|
|
107
|
+
if (source.status !== 'failed' && source.status !== 'dead_lettered') {
|
|
108
|
+
throw new HttpError(409, ERROR_CODES.WEBHOOK_DELIVERY_NOT_REPLAYABLE, `Only failed or dead-lettered deliveries can be replayed (status=${source.status}).`);
|
|
109
|
+
}
|
|
110
|
+
const copy = em.create(WebhookDelivery, {
|
|
111
|
+
webhookId: source.webhookId,
|
|
112
|
+
eventId: source.eventId,
|
|
113
|
+
eventType: source.eventType,
|
|
114
|
+
payload: source.payload,
|
|
115
|
+
status: 'pending',
|
|
116
|
+
attemptCount: 0,
|
|
117
|
+
});
|
|
118
|
+
await em.persistAndFlush(copy);
|
|
119
|
+
return copy;
|
|
120
|
+
}
|
|
121
|
+
async listDeliveries(filter = {}) {
|
|
122
|
+
const em = this.emFactory();
|
|
123
|
+
const where = {};
|
|
124
|
+
if (filter.webhookId)
|
|
125
|
+
where['webhookId'] = filter.webhookId;
|
|
126
|
+
if (filter.status)
|
|
127
|
+
where['status'] = filter.status;
|
|
128
|
+
return em.find(WebhookDelivery, where, {
|
|
129
|
+
orderBy: { createdAt: 'desc' },
|
|
130
|
+
limit: filter.limit ?? 100,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Look up active subscriptions for a given event type — used by the
|
|
135
|
+
* event-bridge to decide which receivers should get a delivery.
|
|
136
|
+
*
|
|
137
|
+
* Feature 062 (contracts/order-webhooks.md §2): `organizationId` is the
|
|
138
|
+
* value extracted from the event payload. Platform-wide subscriptions
|
|
139
|
+
* (`organizationId` NULL) always match; org-bound subscriptions match only
|
|
140
|
+
* their own organization's events and never an event without one
|
|
141
|
+
* (fail closed — Principle XI).
|
|
142
|
+
*/
|
|
143
|
+
async findActiveByEventType(eventType, organizationId) {
|
|
144
|
+
const em = this.emFactory();
|
|
145
|
+
const rows = await em.find(Webhook, { status: 'active' });
|
|
146
|
+
return rows
|
|
147
|
+
.filter((w) => w.eventTypes.includes(eventType))
|
|
148
|
+
.filter((w) => subscriptionReceivesOrganization(w.organizationId ?? null, organizationId))
|
|
149
|
+
.map((w) => ({ webhookId: w.id, url: w.url, secret: w.secret }));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Records an attempted delivery. The worker (webhook-delivery-worker.ts)
|
|
153
|
+
* is the canonical caller via the BullMQ pipeline; this method also lets
|
|
154
|
+
* tests + admin replay flows insert audit rows directly.
|
|
155
|
+
*/
|
|
156
|
+
async recordDelivery(input) {
|
|
157
|
+
// command-coverage-ignore: delivery execution/bookkeeping — the config write
|
|
158
|
+
// is audited separately; this is provider dispatch state.
|
|
159
|
+
const em = this.emFactory();
|
|
160
|
+
const row = em.create(WebhookDelivery, {
|
|
161
|
+
webhookId: input.webhookId,
|
|
162
|
+
eventId: input.eventId,
|
|
163
|
+
eventType: input.eventType,
|
|
164
|
+
payload: input.payload,
|
|
165
|
+
status: input.status,
|
|
166
|
+
attemptCount: input.attemptCount,
|
|
167
|
+
...(input.lastResponseStatus !== undefined
|
|
168
|
+
? { lastResponseStatus: input.lastResponseStatus }
|
|
169
|
+
: {}),
|
|
170
|
+
...(input.lastError !== undefined ? { lastError: input.lastError } : {}),
|
|
171
|
+
});
|
|
172
|
+
await em.persistAndFlush(row);
|
|
173
|
+
return row;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=webhook-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-service.js","sourceRoot":"","sources":["../../../src/backend/services/webhook-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhooks module — manifest backfill (Module Lifecycle, feature 018).
|
|
3
|
+
*
|
|
4
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
5
|
+
* required so the module participates in the registry. No install /
|
|
6
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
7
|
+
* platform-wide migrations.
|
|
8
|
+
*/
|
|
9
|
+
export declare const manifest: {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
dependencies: string[];
|
|
14
|
+
description?: string | undefined;
|
|
15
|
+
acknowledgedDependencies?: {
|
|
16
|
+
moduleId: string;
|
|
17
|
+
port: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}[] | undefined;
|
|
20
|
+
nonBindingDependencies?: {
|
|
21
|
+
moduleId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
24
|
+
reason: string;
|
|
25
|
+
whenAbsent?: string | undefined;
|
|
26
|
+
}[] | undefined;
|
|
27
|
+
activation?: {
|
|
28
|
+
settingCode: string;
|
|
29
|
+
default: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
nonDeactivatable: true;
|
|
32
|
+
reason: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
settings?: {
|
|
35
|
+
moduleCode: string;
|
|
36
|
+
groups: {
|
|
37
|
+
code: string;
|
|
38
|
+
name: string;
|
|
39
|
+
salesChannelCodes?: string[] | undefined;
|
|
40
|
+
isSystemProtected?: boolean | undefined;
|
|
41
|
+
}[];
|
|
42
|
+
settings: {
|
|
43
|
+
code: string;
|
|
44
|
+
name: string;
|
|
45
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
46
|
+
defaultValue: unknown;
|
|
47
|
+
description?: string | undefined;
|
|
48
|
+
groupCode?: string | undefined;
|
|
49
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
50
|
+
salesChannelCodes?: string[] | undefined;
|
|
51
|
+
enumOptions?: string[] | undefined;
|
|
52
|
+
configurationType?: string | undefined;
|
|
53
|
+
hidden?: boolean | undefined;
|
|
54
|
+
}[];
|
|
55
|
+
} | undefined;
|
|
56
|
+
i18n?: {
|
|
57
|
+
bundlesDir: string;
|
|
58
|
+
} | undefined;
|
|
59
|
+
docs?: false | {
|
|
60
|
+
dir: string;
|
|
61
|
+
} | undefined;
|
|
62
|
+
demo?: false | {
|
|
63
|
+
summary: string;
|
|
64
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
65
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
66
|
+
after?: readonly string[] | undefined;
|
|
67
|
+
package?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
actions?: {
|
|
70
|
+
id: string;
|
|
71
|
+
labelKey: string;
|
|
72
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
73
|
+
targetRoute: string;
|
|
74
|
+
keywords: string[];
|
|
75
|
+
weight: number;
|
|
76
|
+
descriptionKey?: string | undefined;
|
|
77
|
+
requiredPermission?: string | undefined;
|
|
78
|
+
}[] | undefined;
|
|
79
|
+
permissions?: {
|
|
80
|
+
code: string;
|
|
81
|
+
label: string;
|
|
82
|
+
module?: string | undefined;
|
|
83
|
+
description?: string | undefined;
|
|
84
|
+
requires?: string[] | undefined;
|
|
85
|
+
}[] | undefined;
|
|
86
|
+
transactionalEmails?: {
|
|
87
|
+
code: string;
|
|
88
|
+
name: string;
|
|
89
|
+
variables: {
|
|
90
|
+
key: string;
|
|
91
|
+
label: string;
|
|
92
|
+
sampleValue?: string | undefined;
|
|
93
|
+
description?: string | undefined;
|
|
94
|
+
}[];
|
|
95
|
+
description?: string | undefined;
|
|
96
|
+
group?: string | undefined;
|
|
97
|
+
}[] | undefined;
|
|
98
|
+
capabilities?: string[] | undefined;
|
|
99
|
+
exclusiveCapabilities?: {
|
|
100
|
+
key: string;
|
|
101
|
+
errorCode: string;
|
|
102
|
+
}[] | undefined;
|
|
103
|
+
errorCodes?: {
|
|
104
|
+
code: string;
|
|
105
|
+
tokens?: string[] | undefined;
|
|
106
|
+
}[] | undefined;
|
|
107
|
+
blocks?: {
|
|
108
|
+
name: string;
|
|
109
|
+
labelKey: string;
|
|
110
|
+
category: string;
|
|
111
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
112
|
+
fields: Record<string, {
|
|
113
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
114
|
+
label?: string | undefined;
|
|
115
|
+
required?: boolean | undefined;
|
|
116
|
+
options?: {
|
|
117
|
+
label: string;
|
|
118
|
+
value: string | number;
|
|
119
|
+
}[] | undefined;
|
|
120
|
+
refKind?: string | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
descriptionKey?: string | undefined;
|
|
123
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
124
|
+
responsiveFields?: string[] | undefined;
|
|
125
|
+
previewIcon?: string | undefined;
|
|
126
|
+
weight?: number | undefined;
|
|
127
|
+
}[] | undefined;
|
|
128
|
+
blockCategories?: {
|
|
129
|
+
key: string;
|
|
130
|
+
titleKey: string;
|
|
131
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
132
|
+
weight?: number | undefined;
|
|
133
|
+
visible?: boolean | undefined;
|
|
134
|
+
}[] | undefined;
|
|
135
|
+
env?: {
|
|
136
|
+
name: string;
|
|
137
|
+
describes: {
|
|
138
|
+
en: string;
|
|
139
|
+
pl: string;
|
|
140
|
+
};
|
|
141
|
+
requirement: {
|
|
142
|
+
kind: "required";
|
|
143
|
+
} | {
|
|
144
|
+
kind: "requiredWhen";
|
|
145
|
+
input: string;
|
|
146
|
+
equals: string;
|
|
147
|
+
} | {
|
|
148
|
+
kind: "optional";
|
|
149
|
+
without: {
|
|
150
|
+
en: string;
|
|
151
|
+
pl: string;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
secret: boolean;
|
|
155
|
+
generable: boolean;
|
|
156
|
+
owner: {
|
|
157
|
+
kind: "platform";
|
|
158
|
+
} | {
|
|
159
|
+
kind: "application";
|
|
160
|
+
application: "admin" | "backend" | "storefront";
|
|
161
|
+
} | {
|
|
162
|
+
kind: "module";
|
|
163
|
+
moduleId: string;
|
|
164
|
+
};
|
|
165
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
166
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
167
|
+
}[] | undefined;
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiHnB,CAAC"}
|