add webhook support

This commit is contained in:
Morten Olsen
2025-09-09 18:31:20 +02:00
parent 829071f7cc
commit 4a539702e9
5 changed files with 86 additions and 0 deletions

View File

@@ -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 };

View File

@@ -0,0 +1,7 @@
import type { DocumentUpsertEvent } from '@morten-olsen/fluxcurrent-core/services/documents/documents.schemas.ts';
type Notifier = {
upsert: (event: DocumentUpsertEvent) => Promise<void>;
};
export type { Notifier };

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -15,6 +15,8 @@ const webhookConfigSchema = z.object({
secret: stringWithEnvSubstitutionSchema.optional(),
});
type WebhookConfig = z.infer<typeof webhookConfigSchema>;
const mqttConfigSchema = z.object({
type: z.literal('mqtt'),
url: stringWithEnvSubstitutionSchema,
@@ -23,6 +25,8 @@ const mqttConfigSchema = z.object({
baseTopic: stringWithEnvSubstitutionSchema,
});
type MqttConfig = z.infer<typeof mqttConfigSchema>;
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<typeof configSchema>;
export type { WebhookConfig, MqttConfig, Config };
export { webhookConfigSchema, mqttConfigSchema, configSchema };