import { Injectable } from '@nestjs/common'; import * as sgMail from '@sendgrid/mail'; import axios from 'axios'; import { IntegrationResult, IntegrationStrategy, EmailAttachment, } from '../integration.strategy'; @Injectable() export class SendGridApiStrategy implements IntegrationStrategy { async sendMessage( to: string, message: string, config: any, ): Promise { try { const { apiKey, fromEmail, accountName, templateId, subject, richText, variables, } = config; console.log('Rich Text:', richText); console.log('Message: ', message); if (!apiKey || !fromEmail) { throw new Error( 'Missing required SendGrid credentials: apiKey and fromEmail are required', ); } sgMail.setApiKey(apiKey); const toArray = Array.isArray(to) ? to : [to]; const fromHeader = accountName ? { email: fromEmail, name: accountName } : fromEmail; const mailData: any = { to: toArray, from: fromHeader, subject: subject || 'Notification', }; // Handle template emails if (templateId) { mailData.templateId = templateId; if (variables) { mailData.dynamicTemplateData = variables; } } else { // Regular email content mailData.html = richText || message; } // Add CC/BCC if provided if (config.cc) { mailData.cc = Array.isArray(config.cc) ? config.cc : [config.cc]; } if (config.bcc) { mailData.bcc = Array.isArray(config.bcc) ? config.bcc : [config.bcc]; } // Handle attachments with validation if (config.attachments) { //FIXME: Temporarily disable attachment validation to allow larger files // EmailAttachmentValidator.validate( // config.attachments, // 30 * 1024 * 1024, // 'SendGrid', // ); mailData.attachments = this.convertAttachmentsToSendGridFormat( config.attachments, ); } if (config.categories) { mailData.categories = Array.isArray(config.categories) ? config.categories : [config.categories]; } if (config.customArgs) { mailData.customArgs = config.customArgs; } if (config.sendAt) { mailData.sendAt = config.sendAt; } const result = await sgMail.send(mailData); return { success: true, provider: 'sendgrid', service: 'API', messageId: result[0].headers['x-message-id'] || undefined, timestamp: new Date(), message: 'Email sent successfully', }; } catch (error) { let errorMessage = 'Unknown error'; if (error.response?.body?.errors) { errorMessage = error.response.body.errors .map((err: any) => err.message) .join(', '); } else if (error.response?.body?.message) { errorMessage = error.response.body.message; } else if (error.message) { errorMessage = error.message; } return { success: false, provider: 'sendgrid', service: 'API', error: errorMessage, timestamp: new Date(), }; } } validateConfig(config: any): boolean { return !!(config.apiKey && config.fromEmail); } async getVerifiedSenders(apiKey: string): Promise<{ success: boolean; data?: Array<{ label: string; value: string; }>; error?: string; }> { try { if (!apiKey) { throw new Error('SendGrid API key is required'); } const response = await axios.get( 'https://api.sendgrid.com/v3/verified_senders', { headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, }, ); const verifiedSenders = response.data.results || response.data || []; // Format the response for dropdown usage with label and value both as from_email const formattedSenders = verifiedSenders .filter((sender: any) => sender.verified === true) .map((sender: any) => { const fromEmail = sender.from_email || sender.email; return { label: fromEmail, value: fromEmail, }; }); return { success: true, data: formattedSenders, }; } catch (error) { let errorMessage = 'Failed to fetch verified senders'; if (error.response?.status === 401 || error.response?.status === 403) { errorMessage = 'Invalid SendGrid API key'; } else if (error.response?.data?.errors) { errorMessage = error.response.data.errors .map((err: any) => err.message) .join(', '); } else if (error.response?.data?.message) { errorMessage = error.response.data.message; } else if (error.message) { errorMessage = error.message; } return { success: false, error: errorMessage, }; } } async getTemplates(config: any): Promise<{ success: boolean; data?: Array<{ label: string; value: string; }>; error?: string; }> { try { if (!config.apiKey) { throw new Error('SendGrid API key is required'); } const response = await axios.get( 'https://api.sendgrid.com/v3/templates?generations=dynamic', { headers: { Authorization: `Bearer ${config.apiKey}`, 'Content-Type': 'application/json', }, }, ); const templates = response.data.result || response.data.templates || []; // Format the response for dropdown usage with template ID in both label and value const formattedTemplates = templates .filter((template: any) => template.generation === 'dynamic') .map((template: any) => ({ label: template.name, value: template.id, })); return { success: true, data: formattedTemplates, }; } catch (error) { let errorMessage = 'Failed to fetch templates'; if (error.response?.status === 401) { errorMessage = 'Invalid SendGrid API key'; } else if (error.response?.status === 403) { errorMessage = 'SendGrid API key does not have required permissions'; } else if (error.response?.data?.errors) { errorMessage = error.response.data.errors .map((err: any) => err.message) .join(', '); } else if (error.response?.data?.message) { errorMessage = error.response.data.message; } else if (error.message) { errorMessage = error.message; } return { success: false, error: errorMessage, }; } } private convertAttachmentsToSendGridFormat( attachments: EmailAttachment[], ): any[] { return attachments.map((attachment) => ({ content: attachment.content, filename: attachment.filename, type: attachment.contentType, disposition: attachment.disposition || 'attachment', content_id: attachment.contentId, })); } }