diff --git a/packages/server/src/notifiers/mqtt/mqtt.ts b/packages/server/src/notifiers/mqtt/mqtt.ts new file mode 100644 index 0000000..92903a1 --- /dev/null +++ b/packages/server/src/notifiers/mqtt/mqtt.ts @@ -0,0 +1,19 @@ +import type { DocumentUpsertEvent } from '@morten-olsen/fluxcurrent-core/services/documents/documents.schemas.js'; + +import type { Notifier } from '../notifier.types.ts'; + +import type { MqttConfig } from '#root/schemas/schemas.config.ts'; + +class MqttNotifier implements Notifier { + #config: MqttConfig; + + constructor(config: MqttConfig) { + this.#config = config; + } + + public upsert = async (event: DocumentUpsertEvent) => { + console.log('upsert', event); + }; +} + +export { MqttNotifier }; diff --git a/packages/server/src/notifiers/notifier.types.ts b/packages/server/src/notifiers/notifier.types.ts new file mode 100644 index 0000000..b97f8d6 --- /dev/null +++ b/packages/server/src/notifiers/notifier.types.ts @@ -0,0 +1,7 @@ +import type { DocumentUpsertEvent } from '@morten-olsen/fluxcurrent-core/services/documents/documents.schemas.ts'; + +type Notifier = { + upsert: (event: DocumentUpsertEvent) => Promise; +}; + +export type { Notifier }; diff --git a/packages/server/src/notifiers/notifiers.ts b/packages/server/src/notifiers/notifiers.ts new file mode 100644 index 0000000..1377c10 --- /dev/null +++ b/packages/server/src/notifiers/notifiers.ts @@ -0,0 +1,17 @@ +import type { DocumentUpsertEvent } from '@morten-olsen/fluxcurrent-core/services/documents/documents.schemas.ts'; + +import type { Notifier } from './notifier.types.ts'; + +class Notifiers { + #notifiers: Notifier[]; + + constructor(notifiers: Notifier[]) { + this.#notifiers = notifiers; + } + + public upsert = async (event: DocumentUpsertEvent) => { + await Promise.all(this.#notifiers.map((notifier) => notifier.upsert(event))); + }; +} + +export { Notifiers }; diff --git a/packages/server/src/notifiers/webhook/webhook.ts b/packages/server/src/notifiers/webhook/webhook.ts new file mode 100644 index 0000000..e0089cf --- /dev/null +++ b/packages/server/src/notifiers/webhook/webhook.ts @@ -0,0 +1,36 @@ +import { createHmac } from 'crypto'; + +import type { DocumentUpsertEvent } from '@morten-olsen/fluxcurrent-core/services/documents/documents.schemas.js'; + +import type { Notifier } from '../notifier.types.ts'; + +import type { WebhookConfig } from '#root/schemas/schemas.config.ts'; + +class WebhookNotifier implements Notifier { + #config: WebhookConfig; + + constructor(config: WebhookConfig) { + this.#config = config; + } + + public upsert = async (event: DocumentUpsertEvent) => { + const body = JSON.stringify(event); + const abortController = new AbortController(); + const hash = this.#config.secret ? createHmac('sha256', this.#config.secret).update(body).digest('hex') : undefined; + await fetch(this.#config.url, { + method: 'POST', + body: JSON.stringify(event), + signal: abortController.signal, + headers: { + 'Content-Type': 'application/json', + 'x-verify': hash ?? '', + }, + }); + + setTimeout(() => { + abortController.abort(); + }, 1000); + }; +} + +export { WebhookNotifier }; diff --git a/packages/server/src/schemas/schemas.config.ts b/packages/server/src/schemas/schemas.config.ts index abea9d8..c18996e 100644 --- a/packages/server/src/schemas/schemas.config.ts +++ b/packages/server/src/schemas/schemas.config.ts @@ -15,6 +15,8 @@ const webhookConfigSchema = z.object({ secret: stringWithEnvSubstitutionSchema.optional(), }); +type WebhookConfig = z.infer; + const mqttConfigSchema = z.object({ type: z.literal('mqtt'), url: stringWithEnvSubstitutionSchema, @@ -23,6 +25,8 @@ const mqttConfigSchema = z.object({ baseTopic: stringWithEnvSubstitutionSchema, }); +type MqttConfig = z.infer; + const oidcConfigSchema = z.object({ type: z.literal('oidc'), issuer: stringWithEnvSubstitutionSchema, @@ -54,4 +58,7 @@ const configSchema = z.object({ notifications: z.array(notificationsConfigSchema).default([]), }); +type Config = z.infer; + +export type { WebhookConfig, MqttConfig, Config }; export { webhookConfigSchema, mqttConfigSchema, configSchema };