export interface IntegrationStrategy { sendMessage( to: string, message: string, config: any, ): Promise; validateConfig(config: any): boolean; createUserSpecificConfig?( baseConfig: any, userIntegrationData?: any, ): any; } export interface IntegrationResult { success: boolean; messageId?: string; provider: string; service: string; error?: string; timestamp: Date; refreshedToken?: string; message?: string; } export interface EmailAttachment { filename: string; content: string; // base64 encoded content contentType: string; // MIME type (e.g., 'application/pdf', 'image/png') disposition?: 'attachment' | 'inline'; // default: 'attachment' contentId?: string; // for inline images } export interface MessageTemplate { subject?: string; body: string; attachments?: EmailAttachment[]; } export class EmailAttachmentValidator { static validate( attachments: EmailAttachment[], maxSizeBytes: number, providerName: string, ): void { let totalSize = 0; for (const attachment of attachments) { // Validate required fields if (!attachment.filename) { throw new Error('Attachment filename is required'); } if (!attachment.content) { throw new Error( `Attachment content is required for ${attachment.filename}`, ); } if (!attachment.contentType) { throw new Error( `Attachment contentType is required for ${attachment.filename}`, ); } // Validate base64 format if (!this.isValidBase64(attachment.content)) { throw new Error( `Invalid base64 content for attachment ${attachment.filename}`, ); } // Calculate size (base64 is ~4/3 of original size) const attachmentSize = (attachment.content.length * 3) / 4; totalSize += attachmentSize; if (totalSize > maxSizeBytes) { const maxSizeMB = Math.floor(maxSizeBytes / (1024 * 1024)); throw new Error( `Total attachment size exceeds ${providerName} limit of ${maxSizeMB}MB`, ); } } } private static isValidBase64(str: string): boolean { try { // Check if string is valid base64 const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; return ( base64Regex.test(str) && Buffer.from(str, 'base64').toString('base64') === str ); } catch { return false; } } }