{"version":3,"file":"webhook-runner.mjs","sources":["../../src/services/webhook-runner.ts"],"sourcesContent":["/**\n * The event hub is Strapi's event control center.\n */\n\nimport createdDebugger from 'debug';\nimport _ from 'lodash';\nimport type { Logger } from '@strapi/logger';\n\nimport type { Modules } from '@strapi/types';\nimport WorkerQueue from './worker-queue';\nimport type { EventHub } from './event-hub';\nimport type { Fetch } from '../utils/fetch';\n\ntype Webhook = Modules.WebhookStore.Webhook;\n\ninterface Config {\n defaultHeaders: Record;\n}\n\ninterface ConstructorParameters {\n eventHub: EventHub;\n logger: Logger;\n configuration?: Record;\n fetch: Fetch;\n}\n\ninterface Event {\n event: string;\n info: Record;\n}\n\ntype Listener = (info: Record) => Promise;\n\nconst debug = createdDebugger('strapi:webhook');\n\nconst defaultConfiguration: Config = {\n defaultHeaders: {},\n};\n\nclass WebhookRunner {\n private eventHub: EventHub;\n\n private logger: Logger;\n\n private config: Config;\n\n private webhooksMap: Map = new Map();\n\n private listeners: Map = new Map();\n\n private queue: WorkerQueue;\n\n private fetch: Fetch;\n\n constructor({ eventHub, logger, configuration = {}, fetch }: ConstructorParameters) {\n debug('Initialized webhook runner');\n this.eventHub = eventHub;\n this.logger = logger;\n this.fetch = fetch;\n\n if (typeof configuration !== 'object') {\n throw new Error(\n 'Invalid configuration provided to the webhookRunner.\\nCheck your server.json -> webhooks configuration'\n );\n }\n\n this.config = _.merge(defaultConfiguration, configuration);\n\n this.queue = new WorkerQueue({ logger, concurrency: 5 });\n\n this.queue.subscribe(this.executeListener.bind(this));\n }\n\n deleteListener(event: string) {\n debug(`Deleting listener for event '${event}'`);\n\n const fn = this.listeners.get(event);\n\n if (fn !== undefined) {\n this.eventHub.off(event, fn);\n this.listeners.delete(event);\n }\n }\n\n createListener(event: string) {\n debug(`Creating listener for event '${event}'`);\n if (this.listeners.has(event)) {\n this.logger.error(\n `The webhook runner is already listening for the event '${event}'. Did you mean to call .register() ?`\n );\n }\n\n const listen = async (info: Event['info']) => {\n this.queue.enqueue({ event, info });\n };\n\n this.listeners.set(event, listen);\n this.eventHub.on(event, listen);\n }\n\n async executeListener({ event, info }: Event) {\n debug(`Executing webhook for event '${event}'`);\n const webhooks = this.webhooksMap.get(event) || [];\n const activeWebhooks = webhooks.filter((webhook) => webhook.isEnabled === true);\n\n for (const webhook of activeWebhooks) {\n await this.run(webhook, event, info).catch((error: unknown) => {\n this.logger.error('Error running webhook');\n this.logger.error(error);\n });\n }\n }\n\n run(webhook: Webhook, event: string, info = {}) {\n const { url, headers } = webhook;\n\n return this.fetch(url, {\n method: 'post',\n body: JSON.stringify({\n event,\n createdAt: new Date(),\n ...info,\n }),\n headers: {\n ...this.config.defaultHeaders,\n ...headers,\n 'X-Strapi-Event': event,\n 'Content-Type': 'application/json',\n },\n signal: AbortSignal.timeout(10000),\n })\n .then(async (res) => {\n if (res.ok) {\n return {\n statusCode: res.status,\n };\n }\n\n return {\n statusCode: res.status,\n message: await res.text(),\n };\n })\n .catch((err) => {\n return {\n statusCode: 500,\n message: err.message,\n };\n });\n }\n\n add(webhook: Webhook) {\n debug(`Registering webhook '${webhook.id}'`);\n const { events } = webhook;\n\n events.forEach((event) => {\n if (this.webhooksMap.has(event)) {\n this.webhooksMap.get(event)?.push(webhook);\n } else {\n this.webhooksMap.set(event, [webhook]);\n this.createListener(event);\n }\n });\n }\n\n update(webhook: Webhook) {\n debug(`Refreshing webhook '${webhook.id}'`);\n this.remove(webhook);\n this.add(webhook);\n }\n\n remove(webhook: Webhook) {\n debug(`Unregistering webhook '${webhook.id}'`);\n\n this.webhooksMap.forEach((webhooks, event) => {\n const filteredWebhooks = webhooks.filter((value) => value.id !== webhook.id);\n\n // Cleanup hanging listeners\n if (filteredWebhooks.length === 0) {\n this.webhooksMap.delete(event);\n this.deleteListener(event);\n } else {\n this.webhooksMap.set(event, filteredWebhooks);\n }\n });\n }\n}\n\n/**\n * Expose a factory function instead of the class\n */\nexport default function createWebhookRunner(opts: ConstructorParameters): WebhookRunner {\n return new WebhookRunner(opts);\n}\n\nexport type { WebhookRunner };\n"],"names":["debug","createdDebugger","defaultConfiguration","defaultHeaders","WebhookRunner","deleteListener","event","fn","listeners","get","undefined","eventHub","off","delete","createListener","has","logger","error","listen","info","queue","enqueue","set","on","executeListener","webhooks","webhooksMap","activeWebhooks","filter","webhook","isEnabled","run","catch","url","headers","fetch","method","body","JSON","stringify","createdAt","Date","config","signal","AbortSignal","timeout","then","res","ok","statusCode","status","message","text","err","add","id","events","forEach","push","update","remove","filteredWebhooks","value","length","constructor","configuration","Map","Error","_","merge","WorkerQueue","concurrency","subscribe","bind","createWebhookRunner","opts"],"mappings":"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"}