{"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<string, string>;\n}\n\ninterface ConstructorParameters {\n  eventHub: EventHub;\n  logger: Logger;\n  configuration?: Record<string, unknown>;\n  fetch: Fetch;\n}\n\ninterface Event {\n  event: string;\n  info: Record<string, unknown>;\n}\n\ntype Listener = (info: Record<string, unknown>) => Promise<void>;\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<string, Webhook[]> = new Map();\n\n  private listeners: Map<string, Listener> = new Map();\n\n  private queue: WorkerQueue<Event, void>;\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","configuration","Map","Error","_","merge","WorkerQueue","concurrency","subscribe","bind","createWebhookRunner","opts"],"mappings":"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"}