/** * Email notification provider (Feature 12, Story 12.5) * * Uses nodemailer for SMTP delivery with HTML templates. */ import type { NotificationPayload, DeliveryResult, NotificationChannel, EmailChannelConfig } from '@agentkitai/agentlens-core'; import type { NotificationProvider } from '../provider.js'; import { createLogger } from '../../logger.js'; const log = createLogger('EmailProvider'); export class EmailProvider implements NotificationProvider { readonly type = 'email'; async send(channel: NotificationChannel, payload: NotificationPayload): Promise { const config = channel.config as unknown as EmailChannelConfig; try { // Dynamic import to avoid hard dependency if nodemailer not installed let nodemailer: any; try { nodemailer = await Function('return import("nodemailer")')(); } catch { return { success: false, channelId: channel.id, channelType: 'email', attempt: 1, error: 'nodemailer not installed' }; } const transporter = nodemailer.createTransport({ host: config.smtpHost, port: config.smtpPort, secure: config.smtpSecure ?? config.smtpPort === 465, auth: config.smtpUser ? { user: config.smtpUser, pass: config.smtpPass } : undefined, }); const html = this.buildHtml(payload); await transporter.sendMail({ from: config.from, to: config.to.join(', '), subject: `[AgentLens ${payload.severity.toUpperCase()}] ${payload.title}`, html, }); return { success: true, channelId: channel.id, channelType: 'email', attempt: 1 }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); log.warn(`Email send failed: ${msg}`); return { success: false, channelId: channel.id, channelType: 'email', attempt: 1, error: msg }; } } async testSend(channel: NotificationChannel): Promise { const testPayload: NotificationPayload = { source: 'alert_rule', severity: 'info', title: 'Test Notification', message: `AgentLens notification channel "${channel.name}" is working correctly.`, metadata: { test: true }, triggeredAt: new Date().toISOString(), ruleId: 'test', ruleName: 'Test', }; return this.send(channel, testPayload); } private buildHtml(payload: NotificationPayload): string { const severityColors: Record = { critical: '#E01E5A', warning: '#ECB22E', info: '#36C5F0', }; const color = severityColors[payload.severity] ?? '#36C5F0'; return `

${payload.title}

${payload.message}

${payload.agentId ? `` : ''}
Source:${payload.source}
Severity:${payload.severity}
Rule:${payload.ruleName}
Agent:${payload.agentId}
Triggered:${payload.triggeredAt}
Sent by AgentLens Notification System
`; } }