Working on notifier system.

This commit is contained in:
charlesgauthereau
2025-11-09 16:47:58 +01:00
parent 411e09f452
commit 7dfd9f02e8
27 changed files with 2755 additions and 81 deletions
+80
View File
@@ -0,0 +1,80 @@
"use server"
// src/notifications/dispatch.ts
import { eq } from 'drizzle-orm';
import { dispatchViaProvider } from './providers';
import type { EventPayload, DispatchResult } from './types';
import * as drizzleDb from "@/db";
import {db} from "@/db";
export async function dispatchNotification(
payload: EventPayload,
policyId?: string,
channelId?: string,
): Promise<DispatchResult> {
// // 1. Get policy + channel
// const policy = await db
// .select({
// policy: alertPolicies,
// channel: notificationChannels,
// })
// .from(alertPolicies)
// .innerJoin(
// notificationChannels,
// eq(alertPolicies.notificationChannelId, notificationChannels.id)
// )
// .where(eq(alertPolicies.id, policyId))
// .then((rows) => rows[0]);
//
// if (!policy) {
// return {
// success: false,
// channelId: '',
// provider: 'unknown' as any,
// error: 'Policy or channel not found',
// };
// }
//
// if (!policy.policy.enabled || !policy.channel.enabled) {
// return {
// success: false,
// channelId: policy.channel.id,
// provider: policy.channel.provider as any,
// error: 'Policy or channel is disabled',
// };
// }
if (channelId){
const channel = await db.query.notificationChannel.findFirst({
where: eq(drizzleDb.schemas.notificationChannel.id, channelId),
})
if (channel){
const config = channel.config;
const result = await dispatchViaProvider(
channel.provider as any,
config,
{ ...payload, timestamp: payload.timestamp || new Date() },
channel.id
);
return {
...result,
channelId: channel.id,
};
}
}
return {
success: false,
channelId,
provider: "smtp",
error: 'Unknown error',
};
}
@@ -0,0 +1,40 @@
"use server"
import type { ProviderKind, EventPayload, DispatchResult } from '../types';
// import { sendSlack } from './slack';
import { sendSmtp } from './smtp';
const handlers: Record<
ProviderKind,
(config: any, payload: EventPayload) => Promise<DispatchResult>
> = {
// slack: sendSlack,
smtp: sendSmtp,
};
export async function dispatchViaProvider(
kind: ProviderKind,
config: any,
payload: EventPayload,
channelId: string
): Promise<DispatchResult> {
const handler = handlers[kind];
if (!handler) {
return {
success: false,
channelId,
provider: kind,
error: `Unsupported provider: ${kind}`,
};
}
try {
return await handler(config, payload);
} catch (err: any) {
return {
success: false,
channelId,
provider: kind,
error: err.message || 'Unknown error',
};
}
}
@@ -0,0 +1,59 @@
"use server"
import type {EventPayload, DispatchResult} from '../types';
import nodemailer from 'nodemailer';
import {render} from "@react-email/render";
import TestEmailSettings from "../../../../emails/TestEmailSettings";
export async function sendSmtp(
config: {
host: string;
port: number;
secure: boolean;
user: string;
password: string;
from: string;
to: string | string[];
},
payload: EventPayload
): Promise<DispatchResult> {
console.log(config)
const transporter = nodemailer.createTransport({
pool: true,
host: config.host,
port: config.port,
// secure: config.secure,
secure: true,
auth: {user: config.user, pass: config.password},
});
const result = await transporter.verify();
console.log(result);
const html = `
<h2>${payload.title}</h2>
<p><strong>Level:</strong> ${payload.level}</p>
<p>${payload.message.replace(/\n/g, '<br>')}</p>
${payload.data ? `<pre>${JSON.stringify(payload.data, null, 2)}</pre>` : ''}
`;
const info = await transporter.sendMail({
from: config.from,
to: Array.isArray(config.to) ? config.to.join(', ') : config.to,
// to: config.from,
subject: `[${payload.level.toUpperCase()}] ${payload.title}`,
html,
// subject: "Portabase",
// html: await render(TestEmailSettings(), {}),
});
console.log(info);
return {
success: true,
provider: 'smtp',
message: `Email sent: ${info.messageId}`,
response: info,
};
}
+19
View File
@@ -0,0 +1,19 @@
// export type ProviderKind = 'slack' | 'smtp';
export type ProviderKind = 'smtp';
export interface DispatchResult {
success: boolean;
channelId?: string;
provider: ProviderKind;
message?: string;
error?: string;
response?: any;
}
export interface EventPayload {
title: string;
message: string;
level: 'critical' | 'warning' | 'info';
timestamp?: Date;
data?: Record<string, any>;
}