@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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/dist/admin/index.d.ts +34 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +45 -0
  6. package/dist/admin/index.js.map +1 -0
  7. package/dist/admin/pages/WebhooksPage.d.ts +3 -0
  8. package/dist/admin/pages/WebhooksPage.d.ts.map +1 -0
  9. package/dist/admin/pages/WebhooksPage.js +220 -0
  10. package/dist/admin/pages/WebhooksPage.js.map +1 -0
  11. package/dist/backend/entities/webhook-delivery.entity.d.ts +29 -0
  12. package/dist/backend/entities/webhook-delivery.entity.d.ts.map +1 -0
  13. package/dist/backend/entities/webhook-delivery.entity.js +108 -0
  14. package/dist/backend/entities/webhook-delivery.entity.js.map +1 -0
  15. package/dist/backend/entities/webhook.entity.d.ts +33 -0
  16. package/dist/backend/entities/webhook.entity.d.ts.map +1 -0
  17. package/dist/backend/entities/webhook.entity.js +91 -0
  18. package/dist/backend/entities/webhook.entity.js.map +1 -0
  19. package/dist/backend/index.d.ts +85 -0
  20. package/dist/backend/index.d.ts.map +1 -0
  21. package/dist/backend/index.js +129 -0
  22. package/dist/backend/index.js.map +1 -0
  23. package/dist/backend/routes.d.ts +26 -0
  24. package/dist/backend/routes.d.ts.map +1 -0
  25. package/dist/backend/routes.js +84 -0
  26. package/dist/backend/routes.js.map +1 -0
  27. package/dist/backend/services/event-bridge.d.ts +54 -0
  28. package/dist/backend/services/event-bridge.d.ts.map +1 -0
  29. package/dist/backend/services/event-bridge.js +43 -0
  30. package/dist/backend/services/event-bridge.js.map +1 -0
  31. package/dist/backend/services/webhook-delivery-worker.d.ts +34 -0
  32. package/dist/backend/services/webhook-delivery-worker.d.ts.map +1 -0
  33. package/dist/backend/services/webhook-delivery-worker.js +70 -0
  34. package/dist/backend/services/webhook-delivery-worker.js.map +1 -0
  35. package/dist/backend/services/webhook-queue.d.ts +24 -0
  36. package/dist/backend/services/webhook-queue.d.ts.map +1 -0
  37. package/dist/backend/services/webhook-queue.js +31 -0
  38. package/dist/backend/services/webhook-queue.js.map +1 -0
  39. package/dist/backend/services/webhook-service.d.ts +82 -0
  40. package/dist/backend/services/webhook-service.d.ts.map +1 -0
  41. package/dist/backend/services/webhook-service.js +176 -0
  42. package/dist/backend/services/webhook-service.js.map +1 -0
  43. package/dist/manifest.d.ts +169 -0
  44. package/dist/manifest.d.ts.map +1 -0
  45. package/dist/manifest.js +122 -0
  46. package/dist/manifest.js.map +1 -0
  47. package/dist/migrations/20260425T091359_webhooks_us7_init.d.ts +22 -0
  48. package/dist/migrations/20260425T091359_webhooks_us7_init.d.ts.map +1 -0
  49. package/dist/migrations/20260425T091359_webhooks_us7_init.js +86 -0
  50. package/dist/migrations/20260425T091359_webhooks_us7_init.js.map +1 -0
  51. package/dist/migrations/20260724T203140_webhooks_org_filter.d.ts +17 -0
  52. package/dist/migrations/20260724T203140_webhooks_org_filter.d.ts.map +1 -0
  53. package/dist/migrations/20260724T203140_webhooks_org_filter.js +30 -0
  54. package/dist/migrations/20260724T203140_webhooks_org_filter.js.map +1 -0
  55. package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.d.ts +28 -0
  56. package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.d.ts.map +1 -0
  57. package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.js +49 -0
  58. package/dist/migrations/20260727T200555_webhooks_drop_external_integrations.js.map +1 -0
  59. package/dist/migrations/index.d.ts +29 -0
  60. package/dist/migrations/index.d.ts.map +1 -0
  61. package/dist/migrations/index.js +33 -0
  62. package/dist/migrations/index.js.map +1 -0
  63. package/docs/webhooks.md +50 -0
  64. package/i18n/en.json +6 -0
  65. package/i18n/pl.json +6 -0
  66. package/package.json +97 -0
  67. 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"}